Components
A component is one kind of thing a mind can believe: a temperature, a pose, a map, a plan. An entity is something that holds components: the room, the body, a person it can see. Together they are the world — everything the mind believes, right now, as plain data.
{
"room": { "temperature": { "celsius": 18.4 }, "heating": { "on": true } },
"self": { "pose": { "position": { "x": 1.2, "y": 0, "z": 0.4 } } }
}
There is nothing else in a cognet's memory. No variables held between ticks, no objects owned by a system. If the mind believes it, it is a component, where any system that declares it can read it.
Declaring one
A component is a plain type in a *.components.ts, *.components.rs or *.components.py
file under types/. Every exported type in the file is a component; unexported ones are
shapes the components use.
// types/body/body.components.ts
interface Vec3 {
x: f64
y: f64
z: f64
}
/** Where the body is, and when that was sensed. */
export interface Pose {
position: Vec3
sensed: f64
}
The file is read, never run. axonl prepare turns every component — whatever language it
was declared in — into types for all three, so a Python system reads a Pose declared in
Rust as an ordinary Python class.
Names come from where it lives
A component's name is its folder plus its type name: Pose in types/body/ is
body/pose. Systems refer to it by the short name, pose, when that is unambiguous.
The same name declared twice — even in two languages — fails at axonl prepare.
| Type | Field types |
|---|---|
| Numbers | f64, f32, u32, i32 (64-bit integers are refused) |
| Text, flags, bytes | string, boolean, bytes |
| Fixed tuples | [f64, f64, f64] |
| Lists and maps | T[], Record<string, T> |
| Large arrays | Float64Array, Float32Array, Int32Array, Uint32Array, Uint8Array |
| Optional | field?: T |
Large arrays — pictures, point clouds, maps — cross between languages as memory, never number by number.
Reading and writing
Systems reach the world through ecs. A query asks for every entity holding the components
named; a write sets one entity's components at once.
const [body] = ecs.query({ components: ["pose"] })
ecs.set({ id: "self", components: { pose: { position: next, sensed: now } } })
A system may only touch the components it declared. See Systems.
A component is just its type
There is nothing else to declare. Every component is readable by any system that declares it, and recorded in every session.
What a part offers the rest of the mind is a convention, not a rule: a folder's
export.components.* file holds what other parts are meant to read; everything else is its
working state. Every read is declared, so axonl debug graph shows exactly who reaches into
whose folder.
Large values are recorded as a digest. A value whose arrays add up to more than 4 KiB — a picture, a cloud, a map — is held in full in the world, and recorded with each array replaced by its kind and length. The session log stays small without marking anything.
One writer
Every component has exactly one system that writes it. Two systems writing pose is refused
before the first tick, naming both. So for every belief in the mind, there is one answer to
"where did this come from?" — and the debugger can give it.
$ axonl debug graph --component pose
{"ok":true,"data":{"name":"body/pose","writtenBy":["body/odometry"],"readBy":["body/map","motor/act"], …}}