Traits
Trait definitions and state machine types for Almadar
How traits work in the Almadar/Orbital architecture - state machines, guards, effects, and cross-orbital communication.
Related: Entities
Overview
In Almadar, a Trait is a state machine that defines behavior for an entity. The fundamental composition is:
Orbital Unit = Entity + Traits + Pages
While Entities define the shape of data, Traits define how that data changes over time through states, transitions, guards, and effects.
Trait Definition
A trait is defined in the .orb schema with the following structure:
{
"name": "TaskManagement",
"category": "interaction",
"linkedEntity": "Task",
"description": "Manages task lifecycle and status changes",
"emits": [
{ "event": "TASK_COMPLETED", "scope": "external" }
],
"listens": [
{ "event": "USER_ASSIGNED", "triggers": "ASSIGN" }
],
"stateMachine": {
"states": [
{ "name": "idle", "isInitial": true },
{ "name": "active" },
{ "name": "completed", "isTerminal": true }
],
"events": [
{ "key": "START", "name": "Start Task" },
{ "key": "COMPLETE", "name": "Complete Task" }
],
"transitions": [
{
"from": "idle",
"to": "active",
"event": "START",
"effects": [["set", "@entity.id", "status", "active"]]
},
{
"from": "active",
"to": "completed",
"event": "COMPLETE",
"guard": ["=", "@entity.assigneeId", "@user.id"],
"effects": [
["set", "@entity.id", "status", "completed"],
["emit", "TASK_COMPLETED", { "taskId": "@entity.id" }]
]
}
]
}
}
Trait Properties
| Property | Required | Description |
|---|---|---|
name | Yes | Trait identifier (PascalCase) |
category | No | Trait category (see below) |
linkedEntity | No | Entity this trait operates on |
description | No | Human-readable description |
emits | No | Events this trait can emit |
listens | No | Events this trait listens for |
stateMachine | Yes | State machine definition |
ticks | No | Scheduled/periodic effects |
config | No | Configuration schema |
Trait Categories
Traits are categorized by their primary purpose:
| Category | Purpose | Typical Effects |
|---|---|---|
interaction | Client-side UI event handling | render-ui, navigate, notify |
integration | Server-side operations | persist, fetch, call-service |
lifecycle | Entity lifecycle management | persist, emit |
gameCore | Game loop and physics | set, emit, ticks |
gameEntity | Game entity behaviors | set, emit, render-ui |
gameUi | Game UI, HUD, controls | render-ui, notify |
Category Examples
Interaction Trait - Handles UI events:
{
"name": "FormInteraction",
"category": "interaction",
"stateMachine": {
"transitions": [{
"event": "SUBMIT",
"effects": [
["render-ui", "main", { "type": "form", "loading": true }],
["emit", "FORM_SUBMITTED", "@payload"]
]
}]
}
}
Integration Trait - Handles server operations:
{
"name": "DataPersistence",
"category": "integration",
"stateMachine": {
"transitions": [{
"event": "SAVE",
"effects": [
["persist", "update", "Task", "@entity.id", "@payload"],
["emit", "DATA_SAVED", { "id": "@entity.id" }]
]
}]
}
}
State Machine
Every trait has a state machine that defines its behavior.
States
States represent the possible conditions of a trait:
{
"states": [
{ "name": "idle", "isInitial": true, "description": "Waiting for input" },
{ "name": "loading", "description": "Fetching data" },
{ "name": "active", "description": "Ready for interaction" },
{ "name": "error", "isTerminal": true, "description": "Error state" }
]
}
| Property | Description |
|---|---|
name | State identifier (lowercase) |
isInitial | Starting state (exactly one required) |
isTerminal | No outgoing transitions expected |
description | Human-readable description |
Events
Events trigger state transitions:
{
"events": [
{ "key": "INIT", "name": "Initialize" },
{ "key": "SUBMIT", "name": "Submit Form", "payload": [
{ "name": "email", "type": "string", "required": true },
{ "name": "name", "type": "string", "required": true }
]},
{ "key": "ERROR", "name": "Error Occurred" }
]
}
| Property | Description |
|---|---|
key | Event identifier (UPPER_SNAKE_CASE) |
name | Display name |
payload | Expected payload schema |
Transitions
Transitions define how states change in response to events:
{
"transitions": [
{
"from": "idle",
"to": "loading",
"event": "SUBMIT",
"guard": ["and", ["!=", "@payload.email", ""], ["!=", "@payload.name", ""]],
"effects": [
["set", "@entity.id", "email", "@payload.email"],
["persist", "create", "User", "@payload"]
]
},
{
"from": ["loading", "active"],
"to": "error",
"event": "ERROR"
}
]
}
| Property | Description |
|---|---|
from | Source state(s) - string or array |
to | Target state (always single) |
event | Triggering event key |
guard | Condition that must pass (optional) |
effects | Effects to execute on transition (optional) |
Multi-source transitions: Use an array for from to handle the same event from multiple states:
{ "from": ["idle", "error"], "to": "loading", "event": "RETRY" }
Guards
Guards are conditions that must evaluate to true for a transition to occur. They use S-expression syntax.
Guard Operators
| Category | Operators |
|---|---|
| Comparison | =, !=, <, >, <=, >= |
| Logic | and, or, not |
| Math | +, -, *, /, % |
| Array | count, includes, every, some |
Guard Examples
// Simple equality
["=", "@entity.status", "active"]
// Compound condition
["and",
["!=", "@payload.email", ""],
["!=", "@payload.name", ""]
]
// Numeric comparison
[">=", "@entity.balance", "@payload.amount"]
// Array check
[">", ["count", "@entity.items"], 0]
// User permission
["=", "@entity.ownerId", "@user.id"]
// Complex guard
["and",
["=", "@entity.status", "pending"],
["or",
["=", "@user.role", "admin"],
["=", "@entity.assigneeId", "@user.id"]
]
]
Guard Bindings
Guards can reference data through bindings (see Entity Bindings):
| Binding | Description |
|---|---|
@entity.field | Current entity field value |
@payload.field | Event payload field |
@state | Current trait state name |
@user.id | Authenticated user ID |
@now | Current timestamp |
Guard Failure
If a guard evaluates to false:
- Transition is blocked
- No effects execute
- State remains unchanged
- Response indicates
transitioned: false
Effects
Effects are actions executed when a transition occurs. They use S-expression syntax.
Effect Types
| Effect | Server | Client | Purpose |
|---|---|---|---|
render-ui | Ignored | Executes | Display pattern to UI slot |
navigate | Ignored | Executes | Route navigation |
notify | Ignored | Executes | Show notification/toast |
fetch | Executes | Ignored | Query database |
persist | Executes | Ignored | Create/update/delete data |
call-service | Executes | Ignored | Call external API |
emit | Executes | Executes | Publish event |
set | Executes | Executes | Modify entity field (supports increment/decrement via S-expressions) |
Dual Execution Model
Traits execute on both client and server simultaneously:
┌─────────────────────────────────────────────────────────────┐
│ Client Server │
│ ─────── ────── │
│ render-ui ✓ render-ui → clientEffects │