<!-- https://minecraftdocs.dev/systems/entities/attributes — this page as markdown. The book's index: https://minecraftdocs.dev/llms.txt; the whole book in one file: https://minecraftdocs.dev/llms-full.txt -->

# Attributes

> Verified against **Minecraft 26.3** · Part VI · Strength II is applied to a player: one modifier lands on one attribute, no attribute packet goes on the wire, and the swing three seconds later reads the new number.

Strength II lands on you and thirty seconds later it wears off. In between,
one `AttributeModifier` — an amount of +6, an operation of
`AttributeModifier.Operation.ADD_VALUE`, an id of *effect.strength* — sits on
one attribute of one entity, and every swing you make asks for that attribute
and gets a bigger number back. The same mechanism is behind armour points, a
horse's jump strength, the extra reach of a creative-mode player and the
distance at which a mob notices you: *ask a question, get a number*, cheaply,
with a defined order of operations. What is surprising is what does not
happen. **Strength II sends no attribute packet at all.** The effect itself
goes out, but eight of the forty registered attributes are not client-syncable
and `Attributes.ATTACK_DAMAGE` is one of them, so for the whole thirty seconds
your own client's copy of your attack
damage sits at the base value it was born with — 1.0 — and nothing ever tells
it otherwise.

> **A different system with the same words.** `world/attribute` is
> *environment* attributes — per-position world properties like the sky's colour
> ([environment attributes and
> timelines](../world/environment-attributes-and-timelines#the-stack-a-value-falls-through)) — with its own
> registries and its own class also named `AttributeModifier`. Nothing on this
> page refers to it.

## The cast

| class | what it decides | thread |
|---|---|---|
| `Attribute` | one named number's default, its description id, its `Attribute.Sentiment` (tooltip colour only) and the one boolean that decides whether the client is ever told | built in the `Attributes` class initialiser, read from every thread after |
| `RangedAttribute` | the minimum and the maximum, and `RangedAttribute.sanitizeValue` — the clamp. The only subclass, and every registered attribute is one | as above |
| `AttributeSupplier` | what attributes an `EntityType` has at all, and their base values. Frozen | built at class-init, inside `DefaultAttributes` |
| `AttributeMap` | which of two dirty sets a change lands in, and therefore whether a packet is sent | the Server thread for mutations, the Render thread for the mirror |
| `AttributeInstance` | the number: a base value, three modifier indices, a dirty flag and a cache | as above |
| `AttributeModifier` | an `Identifier`, an amount and an operation. A record, and inside an instance the identifier alone is its identity | immutable, shared |
| `LivingEntity` | when the update set drains, and what reacts to a change | both sides, in `LivingEntity.tick` |
| `ServerEntity` | when the sync set drains and what goes on the wire | the Server thread, in the level tick's *chunkSource* phase |

## Four objects, two dirty sets, and one list that is neither

```mermaid
flowchart TD
    ATTR["Attribute, always a RangedAttribute: a range, and a syncable flag"]
    SUP["AttributeSupplier: one frozen prototype map per EntityType"]
    MAP["AttributeMap: one per LivingEntity, only what was asked for"]
    INST["AttributeInstance: a base value, a cached value, a dirty flag"]
    UPD["AttributeMap.attributesToUpdate: every dirtied attribute"]
    SYNC["AttributeMap.attributesToSync: only the syncable ones"]
    MOD["AttributeMap.onAttributeModified"]
    PAIR["AttributeMap.getSyncableAttributes: not a set, a filter"]
    REACT["LivingEntity.refreshDirtyAttributes, in the entity's own tick"]
    SEND["ServerEntity.sendDirtyEntityData, the ServerLevel.chunkSource phase"]
    WIRE["ClientboundUpdateAttributesPacket"]

    ATTR -- "registered once" --> SUP
    SUP -- "one per EntityType" --> MAP
    MAP -- "AttributeMap.getInstance" --> INST
    INST -- "AttributeInstance.setDirty" --> MOD
    MOD -- "always" --> UPD
    MOD -- "and if syncable" --> SYNC
    MAP -- "when tracking starts" --> PAIR
    UPD --> REACT
    SYNC --> SEND
    SEND --> WIRE
    PAIR --> WIRE
```

*The four objects and the three collections — and every collection is the
`AttributeMap`'s, which is the thing to look at: the instance only calls
`AttributeInstance.setDirty`, and the map decides which sets that lands in.*

Four objects carry the whole system — the `Attribute`, the frozen
`AttributeSupplier` per type, the `AttributeMap` per entity, and the
`AttributeInstance` per number asked for. The instance keeps three
lists of its own, and they belong to [a later section](#the-instance-three-indices-and-one-cached-number);
the three in the figure all belong to the map.

Two of them are the dirty sets, and they are **not a partition**:
`AttributeMap.onAttributeModified` always adds to the update set and
*additionally* to the sync set when the attribute is syncable, so a syncable
attribute is in both and a non-syncable one is in the update set alone. The
third is not a dirty set at all:
`AttributeMap.getSyncableAttributes` filters the whole live map every time it is
asked, and it exists for one caller — `ServerEntity.sendPairingData`, which has
to describe an entity to a player who has never seen it and so cannot work from
what has changed since last time.

## Forty numbers, every one of them clamped

The forty constants of `Attributes` register themselves in their own static
field initialisers, into `BuiltInRegistries.ATTRIBUTE` under
`Registries.ATTRIBUTE`. `Attributes.bootstrap` does nothing but return
`Attributes.MAX_HEALTH`, and exists only as the class-loading trigger
`BuiltInRegistries` needs — which is also why a data pack can *reference* an
attribute but never add one.

Every one of the forty is a `RangedAttribute`, and `RangedAttribute` is the
only subclass of `Attribute`, so every attribute in the game has a minimum
and a maximum and every computed value passes through one clamp. Several of
those bounds are the reason a mechanic behaves the way it does:
`Attributes.MAX_HEALTH` has a minimum of 1, so no entity's maximum health can
reach zero, and `Attributes.KNOCKBACK_RESISTANCE` has a minimum of −2, so
*amplified* knockback is a legal value rather than a bug. The full list — id,
constant, default, minimum, maximum, syncable, sentiment — is
[the attribute table](../../reference/attributes).

### The syncable flag, and the eight that never travel

What has to be said here is the syncable flag, because it explains most of
what a client and a server disagree about. **Eight** of the forty never reach
the client: `Attributes.ATTACK_DAMAGE`, `Attributes.ATTACK_KNOCKBACK`,
`Attributes.KNOCKBACK_RESISTANCE`, `Attributes.FOLLOW_RANGE`,
`Attributes.TEMPT_RANGE`, `Attributes.SPAWN_REINFORCEMENTS_CHANCE` — whose
registry id is *spawn_reinforcements*, disagreeing with its own constant name
— and the pair `Attributes.WAYPOINT_TRANSMIT_RANGE` and
`Attributes.WAYPOINT_RECEIVE_RANGE`. An attribute is syncable only because
its registration line called `Attribute.setSyncable`, and that setter is
public and has no freeze behind it, on an object that lives in a registry.
Nothing calls it after bootstrap. Nothing stops it either.

The client is not left with a blank where an unsyncable attribute should be,
which is the part that makes the silence hard to notice: both sides run the
same `Attributes` class initialiser and build the same `DefaultAttributes`
prototypes, so your client's copy of your attack damage holds the prototype's
1.0 from the moment the entity exists. It is not missing. It is a number that
was right once and is never corrected — and the client reads it: your own
client runs `Player.attack` for your swing, which asks for your attack damage
([authority](authority#three-cases-read-on-both-sides)).

## The prototype, frozen at class-init

`DefaultAttributes` holds one `AttributeSupplier` per `EntityType`, each
built by chaining builders: `LivingEntity.createLivingAttributes` is the
twenty-six-entry base, `Mob.createMobAttributes` adds
`Attributes.FOLLOW_RANGE` at 16, `Monster.createMonsterAttributes` adds
`Attributes.ATTACK_DAMAGE` and nothing else, and each species' own builder
finishes the job. `AttributeSupplier.Builder.build` keeps the **last** entry
for a repeated attribute, so a species builder does not have to know what its
ancestors declared: of the five attributes `Zombie.createAttributes` names,
four are already in the chain and are simply overwritten — attack damage from
the monster builder, follow range from the mob builder, movement speed and
armour from the twenty-six-entry living base — and only
`Attributes.SPAWN_REINFORCEMENTS_CHANCE` is new. Anything
outside `MobCategory.MISC` with no supplier at all is logged by
`DefaultAttributes.validate`.

A prototype is frozen in the sense that `AttributeSupplier.Builder.build`
arms a callback that throws on any later write: reading one is fine, dirtying
one throws. Reading an attribute the type does not have is also fatal — the
fallback in `AttributeSupplier` raises rather than returning a default, which
is why `LivingEntity.getAttribute` is nullable and
`LivingEntity.getAttributeValue` is not.

`Player.createAttributes` is on `Player`, not on `Avatar`. That intermediate
class ([entity anatomy](entity-anatomy#the-tree-and-the-class-that-was-inserted-into-it))
owns the player-shaped hitbox but not the attribute set, so `Mannequin` — the other `Avatar` —
is registered with `LivingEntity.createLivingAttributes` and gets the plain
living set, including the registry's default movement speed of 0.7 rather
than a player's 0.1. The default is not a dead value: the wandering trader
is registered with the bare `Mob` builder, which sets no speed either, and
walks on it.

## The map, and which set a change lands in

`AttributeMap` starts empty. `AttributeMap.getValue` and
`AttributeMap.getBaseValue` answer from the prototype when the entity has no
instance of its own, and change nothing. `AttributeMap.getInstance` is
different: it creates the instance on demand through
`AttributeSupplier.createInstance`, which copies the frozen template with
`AttributeInstance.replaceFrom` — which ends in `AttributeInstance.setDirty`.
So **asking for an instance is a mutation**: the first call to
`LivingEntity.getAttribute` for a syncable attribute enqueues it for
broadcast before any modifier exists, and a share of the attribute packets a
busy server sends are caused by something merely asking.

### The two sets drain in two different phases

The two sets drain in different phases of the same tick, and that is where
the visible lag comes from. `ServerEntity.sendDirtyEntityData` is reached
from `ChunkMap.tick`, which runs inside `ServerLevel.tick`'s *chunkSource*
phase — **before** the *entities* phase ([the level
tick](../server/server-level-tick#the-broadcast-which-is-why-entities-are-a-tick-behind)). An attribute dirtied during an
entity's own tick (equipment, an effect, powder snow, anything in
`ServerPlayer.updatePlayerAttributes`) has therefore already missed this
tick's send, and a dirty *attribute* set is not one of the three things that
open `ServerEntity.sendChanges`'s gate ([synched entity
data](synched-entity-data#the-gate-that-holds-a-packet-back) owns that
gate), so it waits for the next tick whose
count is a multiple of the entity's `EntityType.updateInterval` (up to two
ticks for a player, whose interval is 2, and up to three for the default 3),
unless something else opens the gate first. Only a mutation made *before* the
chunk-source phase — a player's command, an interaction handled out of the
packet queue at the top of the server tick — can reach the wire in the tick that produced
it, and then only if the gate opens. It is
the same phase ordering that puts a block entity's writes a tick late
([block entities](../blocks/block-entities#a-furnace-tells-nobody-anything)).

The update set drains in the entity's own tick — the entities phase for a mob,
the connection phase for a player — in
`LivingEntity.refreshDirtyAttributes`, which calls
`LivingEntity.onAttributeUpdated` once per dirtied attribute and then clears
the set. That hook has exactly four branches: clamp health down to a reduced
maximum health, clamp absorption, call `Entity.refreshDimensions` on a scale
change, and register or unregister the transmitted waypoint with the
`ServerWaypointManager`. Subclasses add two more —
`ServerPlayer.onAttributeUpdated` takes the *receive* half of the waypoint
pair, and `Mob.onAttributeUpdated` recomputes the pathfinder's node budget
through `PathNavigation.updatePathfinderMaxVisitedNodes` on a change to
`Attributes.FOLLOW_RANGE` **or** `Attributes.TEMPT_RANGE`
([pathfinding](pathfinding#the-budget-which-is-also-the-map) owns that budget).

`LivingEntity.refreshDirtyAttributes` is called from `LivingEntity.tick` with
no side check, so the **client** runs `LivingEntity.onAttributeUpdated` too,
clamping health and resizing an entity whose scale changed — which is why the
waypoint branch inside it is the one that has to test for a `ServerLevel`
explicitly.

### A frozen mob is the worked example

Everything above has a cost attached, and the cheapest way to see it is a mob
standing in powder snow. `LivingEntity.aiStep` calls
`LivingEntity.removeFrost` and `LivingEntity.tryAddFrost` back to back,
server-side, with no test for whether anything changed. Each has a gate — the
remove only dirties when the modifier is there, the add needs a
non-air block underfoot *and* a non-zero frozen counter — but when both hold,
the pair destroys and re-creates a modifier on `Attributes.MOVEMENT_SPEED`,
dirtying a **syncable** attribute twenty times a second and re-sending that
entity's whole movement-speed modifier list for as long as it stays frozen.
Compare `ServerPlayer.updatePlayerAttributes`, which runs just as often but
uses `AttributeInstance.addOrUpdateTransientModifier` with a constant modifier
object, and so dirties nothing after the first tick. The whole difference is
whether each write dirties anything at all.

### What the map writes to disk

Two more things the map decides, and between them they answer *why did my
`/attribute` change survive death?* `AttributeMap.pack` writes **every
instantiated instance**, base value included, so a base value set by command
persists with no modifier attached to carry it — `AttributeInstance.pack`
writes only the permanent modifiers of each. And on respawn,
`ServerPlayer.restoreFrom` always calls `AttributeMap.assignBaseValues` but
calls `AttributeMap.assignPermanentModifiers` only on a *full* restore:
returning from the End, not an ordinary death. Base values always come
across, a command-added modifier only sometimes.

## The instance: three indices and one cached number

An `AttributeInstance` keeps its modifiers three times over: bucketed by
`AttributeModifier.Operation` for the arithmetic, indexed by `Identifier` for
lookup and duplicate detection, and a second id-index of the *permanent* ones
for saving. A modifier's identity is its `Identifier` alone — there is no
UUID and no name — so two systems that pick the same identifier for the same
attribute collide, and `AttributeInstance.addTransientModifier` and
`AttributeInstance.addPermanentModifier` **throw** rather than silently
overwrite. `AttributeInstance.addOrUpdateTransientModifier` and
`AttributeInstance.addOrReplacePermanentModifier` are the safe forms. Vanilla
mostly removes by id before it adds, or uses the safe forms; three mob classes
and `AttributeCommand` guard with `AttributeInstance.hasModifier` instead.

Transient versus permanent is about keeping: both kinds sit in the
same indices, both affect the value identically, both go on the wire, and
only the permanent ones are packed, or carried across a return from the End. Mob-effect modifiers are added
permanently, and that is the only reason an effect's modifier survives a
reload ([status
effects](../player/status-effects#what-survives-a-save-and-why-the-modifiers-do)). On
the client, meanwhile, *every* modifier is transient, because
`ClientPacketListener.handleUpdateAttributes` sets the base value, wipes the
whole modifier list and re-adds the incoming ones with
`AttributeInstance.addTransientModifier`. A client attribute map is never
persisted.

`AttributeInstance.getValue` recomputes through
`AttributeInstance.calculateValue` only when the dirty flag is set, and the
flag starts true, so the first read always computes — three passes and one
clamp:

```mermaid
flowchart TD
    B["the base value: the prototype's, or whatever has set it since"]
    P1["pass 1: every ADD_VALUE modifier, added"]
    P2["pass 2: every ADD_MULTIPLIED_BASE modifier, off the same base"]
    P3["pass 3: every ADD_MULTIPLIED_TOTAL modifier, off the running total"]
    C["RangedAttribute.sanitizeValue, once"]
    O["AttributeInstance.cachedValue, to the next AttributeInstance.setDirty"]
    B --> P1 --> P2 --> P3 --> C --> O
```

*The three passes, in the order they always run. What separates pass 2 from
pass 3 is the two words on their boxes: `the same base` against `the running
total`.*

That difference is the whole of the arithmetic. Every
`AttributeModifier.Operation.ADD_MULTIPLIED_BASE` modifier reads the base as pass 1 left it, so two of
them do **not** compound — +50% and +50% is +100%. Every
`AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL` modifier reads the previous one's output, so two of
those **do** — +50% and +50% is +125%. The clamp runs once at the end:
`RangedAttribute.sanitizeValue` collapses a not-a-number result to the
minimum and otherwise holds the value between the minimum and the maximum.
Operation order is therefore global, not insertion order, and intermediate
values are never clamped. Within a bucket, iteration order is a hash map's —
safe only because each bucket's arithmetic is commutative.

### …except in the other implementation, which is insertion-ordered

`ItemAttributeModifiers.compute` is a second, disagreeing implementation of
the same idea. It walks an item's `ItemAttributeModifiers.Entry` list in
declaration order, applying each entry's operation to the running total as it
goes, with no three-pass grouping at all. It is not a duplicate of
`AttributeInstance.calculateValue` and it does not agree with it. Its one
caller in the whole game is `Mob.getApproximateAttributeWith` — the "would
this weapon or armour be better than what I have?" estimate a mob makes when
deciding whether to pick an item up.

## Where the modifiers come from

Equipment is the busiest source. `LivingEntity.detectEquipmentUpdates`, in
the server-only half of `LivingEntity.tick`, only dispatches;
`LivingEntity.collectEquipmentChanges` does the work, adding each incoming
stack's modifiers as *transient* (removing by id first) through
`ItemStack.forEachModifier`, which merges
`DataComponents.ATTRIBUTE_MODIFIERS` with the enchantment modifiers from
`EnchantmentHelper.forEachModifier`. Exactly eight vanilla enchantments carry
`EnchantmentEffectComponents.ATTRIBUTES` — fire and blast protection,
respiration, aqua affinity, depth strider, swift sneak, sweeping edge,
efficiency — and `EnchantmentAttributeEffect` has a second, location-based
path through `EnchantmentAttributeEffect.onChangedBlock` that exactly one
uses: soul speed, registered under
`EnchantmentEffectComponents.LOCATION_CHANGED` instead.

The rest, in one breath: `MobEffect` declarations, permanently and scaled by
amplifier; item components, whose `ItemAttributeModifiers.Display` decides
whether *and how* a tooltip line appears — its default form adds the reader's
own base value back in for `Item.BASE_ATTACK_DAMAGE_ID` and
`Item.BASE_ATTACK_SPEED_ID`, so a weapon shows a total rather than a bonus;
`SetAttributesFunction` in loot tables; `Zombie.handleAttributes` at spawn;
`LivingEntity.setSprinting`; `ServerPlayer.updatePlayerAttributes`; and
`AttributeCommand`, whose *modifier add* is **permanent**, and so saved.

One packet, one direction: `ClientboundUpdateAttributesPacket`, server to
every tracking player **and to the entity itself** when it is a player — which is why your own
client has a live attribute map at all. At most 128 attributes fit in one
(checked on encode as well as decode), each snapshot carrying the attribute
holder, the base value and the complete, uncapped modifier list. There is no
serverbound attribute packet.

## Strength II lands, and no attribute leaves the server

Put the four objects, the two sets and the two phases together and the whole
system runs once, end to end, for a number the wire never hears about:

```mermaid
sequenceDiagram
    participant EffC as EffectCommands
    participant LE as LivingEntity
    participant ME as MobEffect
    participant AttrM as AttributeMap
    participant AttrI as AttributeInstance
    participant SE as ServerEntity

    EffC->>LE: addEffect, Strength at amplifier 1
    LE->>LE: onEffectAdded, guarded server-side
    LE->>ME: addAttributeModifiers, the map and the amplifier
    ME->>AttrM: getInstance, Attributes.ATTACK_DAMAGE
    AttrM->>AttrI: replaceFrom, inside AttributeSupplier.createInstance, if new
    AttrI->>AttrM: onAttributeModified, into the update set, if new
    ME->>AttrI: removeModifier, then addPermanentModifier at plus 6
    AttrI->>AttrM: onAttributeModified, into the update set
    Note over LE,SE: not syncable, so the sync set stays empty
    rect rgba(0, 0, 0, 0.04)
    Note over LE,SE: the next server tick: the ServerLevel.chunkSource phase, then the player's own tick
    SE->>SE: sendDirtyEntityData finds an empty attribute set
    LE->>LE: refreshDirtyAttributes drains the update set
    end
    Note over LE,AttrI: three seconds later, inside Player.attack
    LE->>AttrM: getValue, Attributes.ATTACK_DAMAGE
    AttrM->>AttrI: getValue, dirty, so calculateValue
    AttrI-->>AttrM: 1.0 base, plus the sword's, plus 6.0
    AttrM-->>LE: the same number
```

*The lane to watch is `ServerEntity`: it is reached, finds the attribute sync
set empty, and sends no attribute packet. The effect itself does go out, as a
mob-effect packet when it is added and as the swirl in the entity data, but
the attack damage never does.*

`MobEffects.STRENGTH` is declared with one attribute modifier: +3 on
`Attributes.ATTACK_DAMAGE`, `AttributeModifier.Operation.ADD_VALUE`, under
the id *effect.strength*, and the amount is multiplied by amplifier + 1 when
the modifier is built — so Strength II is **+6**. (`MobEffects.WEAKNESS` is
the same construction at −4.) `LivingEntity.addEffect` puts the instance into
the active list and calls `LivingEntity.onEffectAdded`, which is guarded
server-side and hands the entity's `AttributeMap` to
`MobEffect.addAttributeModifiers`. If the effect was already running, the
path is `LivingEntity.onEffectUpdated` instead, which removes and re-adds and,
unlike the add path, refreshes the dirty attributes on the spot.

`MobEffect.addAttributeModifiers` calls `AttributeMap.getInstance`, and that
is where the instance for `Attributes.ATTACK_DAMAGE` is born if nothing has
asked for it yet (a player holding a sword already has one, made when the
sword was equipped), copying the frozen prototype's base value: 1.0 for a
player, 3.0 for a zombie. Creating it dirties it, before any modifier exists.
Then the effect removes its own id, which dirties nothing when the id is not
there, and adds the +6 with `AttributeInstance.addPermanentModifier` — the
remove is not optional, since the plain add throws on a duplicate id — which
dirties it. Each time the
callback adds to the update set and consults the syncable flag before touching
the sync set, so the sync set stays empty and **no attribute packet is sent at
all**. Next tick, `ServerEntity.sendDirtyEntityData` finds no attribute to send
(it does send the swirl the effect put in the entity data), and
`LivingEntity.onAttributeUpdated` matches none of its four branches.

Three seconds later `Player.attack` asks `LivingEntity.getAttributeValue` for
the attack damage. The instance is dirty, so `AttributeInstance.calculateValue`
runs: the addition bucket holds the sword's *base_attack_damage* modifier
from `DataComponents.ATTRIBUTE_MODIFIERS` and the effect's +6, the two
multiplication buckets are empty, the clamp passes, the result is cached. The
rest of the swing — cooldown scale, enchantment bonuses, the crit — belongs
to [the sword swing](../player/the-sword-swing#the-damage-one-number-two-curves-one-order). Expiry runs it backwards:
`LivingEntity.onEffectsRemoved` calls `MobEffect.removeAttributeModifiers`,
which removes by id from all three indices and dirties one last time.

### Swap Strength for Speed and the missing limb appears

`MobEffects.SPEED` targets `Attributes.MOVEMENT_SPEED` with
`AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL`, and movement speed *is*
syncable. Everything above is identical until the callback, which now fills
the sync set too. `ServerEntity.sendDirtyEntityData` drains it in the next
chunkSource phase and emits a `ClientboundUpdateAttributesPacket` carrying
the base value and **every** modifier on that attribute — sprinting, powder
snow, the effect, all of them — and `ClientPacketListener.handleUpdateAttributes`
rebuilds the client's instance from scratch. That difference, one boolean on
the `Attribute`, is the whole design.

## Where to look

`Attributes` is forty field initialisers and the syncable flag is visible in
each of them, so start there; `DefaultAttributes` and
`AttributeSupplier.Builder.build` are the prototypes and the freeze. Then read
the three methods this page turns on, in this order:
`AttributeMap.getInstance`, because asking is a mutation;
`AttributeMap.onAttributeModified`, the four lines that decide which set a
change lands in; and `AttributeInstance.calculateValue`, the three passes and the clamp. `LivingEntity.refreshDirtyAttributes` and
`ServerEntity.sendDirtyEntityData` are where the two sets drain, in different
phases. Two doors: `ItemAttributeModifiers.compute`, the second and disagreeing
implementation of the arithmetic, and `AttributeCommand`, whose *modifier add*
is the plainest way a player can make a permanent modifier by hand.

---

*Rules: names, never code · how the system works, not how the code reads ·
newest version only · every backticked name passes `tools/verify_names.py`.*
