Skip to the content.

Naming conventions

Five rules govern names across stackchan-core (and the firmware / sim that consume it). Each is grounded in prior art from a similar codebase — citations inline so future contributors can re-read the source if a rule needs to bend.

Rule A — Intent variants

Intent describes the avatar’s current state. Use:

The split mirrors Embassy’s UsbDeviceState (Configured, Addressed, Disabled — externally caused → past-participle) and the Unity / Unreal / Spine animation-state-machine convention (Idle / Walking / Running — own activity → gerund).

Avoid bare verbs (Listen, Wake) and noun-adjective compounds (HearingLoud) — they read as an action being requested rather than a state being held.

Rule B — Modifier names

Two grammatical classes by what the modifier does to the avatar:

The <Output> half names the field being written, not the specific value. EmotionFromVoice writes mind.affect.emotion — the doc comment specifies the value(s) it picks (Happy on sustained voice). Same for HeadFromIntent: the doc comment specifies which intent variants it reacts to (Startled).

No *Modifier suffix — the trait registry is typed, so the suffix is redundant. (The Bevy team explicitly dropped the matching *System suffix; see bevy#2804.)

Rule C — Skill names

Skills are long-running detection routines. Use:

The split mirrors BehaviorTree.CPP’s grammatical split between conditions (predicates / state) and actions (imperative verbs), and matches Cozmo’s behavior naming (FindFaces, StackBlocks).

No *Skill / *Detector / *Recognizer suffix.

Rule D — ChirpKind and event variants

Bare noun for the event itself: Pickup, Wake, Startle. Past participle for transitions: CameraModeEntered, CameraModeExited. The *Alert suffix is acceptable when the chirp announces a condition, not an event: LowBatteryAlert.

Matches serde_json::Value (bare nouns) and the animation-state-machine OnEntered / OnExited convention.

Avoid *Event suffix — the enum name (ChirpKind, EmotionFromIntent, etc.) carries the kind information.

Rule E — Cause / state pairing (OverrideSourceIntent)

When a cause enum and a state enum describe the same root concept, use the noun form for the cause and the past-participle form for the state:

Cause (OverrideSource) Resulting state (Intent)
Pickup PickedUp
Shake Shaken
Voice Listening
Startle Startled
BodyTouch Petted
FaceTouch (no intent — hold only)
Remote (no intent — hold only)
Ambient (no intent — emotion only)
LowBattery (no intent — emotion only)

The cause noun and the state past-participle are different grammatical forms of the same concept, so the pair reads naturally in context (source = Pickup, intent = PickedUp) without colliding on identifier.

Source-of-truth

All names on main conform to the rules above. Canonical lists live in the source — read these instead of trying to keep an inline inventory in sync:

Forward guidance

Empty Director phases (Perception, Cognition, Speech, Output)

When modifiers land in these phases, apply Rule B. Likely shapes:

New Skills

Apply Rule C: gerund for recognizers, verb-object for actors. Examples for likely future skills:

New Intents

Apply Rule A:

New cause / state pairs

Apply Rule E. If a new sensor produces both a cause-side override hold and an intent-side state, name them as the noun / past-participle pair (e.g., OverrideSource::TrackingIntent::Tracked).

Why no *Modifier / *Skill suffix

The trait registry is typed (Director::add_modifier(&mut dyn Modifier)), so the type system already carries the role. The Bevy ecosystem proved the suffix is redundant after they dropped *System (discussion). Unreal keeps *Component only because its registry uses untyped UObject reflection — not our shape.

When to break a rule

These rules optimize for reader clarity given the current set of modifiers / skills / intents. If a future name would obey the rule but read worse than a deliberate exception (e.g., a modifier whose behavior fundamentally isn’t a translator and isn’t autonomous — something genuinely third-shape), make the exception, document the why in the module-level doc comment, and update this file with the new pattern if it recurs.

The convention exists to be useful, not to be bureaucratic.