Skip to content

Supporting Class Specifications

Full specifications for the 13 supporting classes that Entity, System, State, and Interaction compose. Each follows the same template as the four core classes (classes/*.md) at a scope appropriate to a smaller, more focused class.


Identity

General. Description: the stable identifier for an Entity/System across its lifetime, independent of mutable State. Design rationale: separated from Entity so identity-comparison and identity-generation policy can evolve (e.g. swap UUID4 for a content hash) without touching Entity. Scientific meaning: "is this the same thing I measured before?" SDK purpose: single source of truth for equality/hashing across the whole ontology.

Relationships. Parent: none. Children: none expected. Interfaces: Comparable. Collaborators: Entity, System.

Attributes. | Attribute | Type | Explanation | |---|---|---| | id | str | Canonical string identifier, unique within scope. | | uuid | UUID | Machine-generated universally unique form. | | scope | str \| None | Optional namespace (e.g. a System id) the uniqueness guarantee is bounded to. | | alias | str \| None | Optional human-friendly alternate name (non-unique). |

Properties. is_scoped (read-only, scope is not None).

Behaviors. create(), compare(), matches(other_or_str).

Methods. - create(scope: str | None = None, alias: str | None = None) -> Identity — generates id/uuid; no preconditions; never raises. - compare(other: Identity) -> ComparisonResult — equality by (scope, id) pair. - matches(value: str | Identity) -> bool — convenience equality check against a raw string or another Identity.

Private Helpers. _generate_uuid(), _normalize_id(raw: str) -> str.

Lifecycle. Immutable value object — no lifecycle machine.

Events. IdentityGenerated (optional, low-value; typically not published).

Exceptions. IdentityError (base) → InvalidIdentityError (malformed id), DuplicateIdentityError (raised by the registrar, e.g. System.add_entity, not by Identity itself).

Interfaces Implemented. Comparable.

Validation. id must be non-empty and match the identifier grammar in constants.py (ID_PATTERN).

Serialization. Trivial flat value; all formats supported.

Thread Safety. Immutable → inherently thread-safe.

Logging/Metrics. None dedicated; identity generation is cheap and high-frequency, not separately instrumented.


Property

General. Immutable, intrinsic, typed characteristic of an Entity (e.g. mass, symbol). Design rationale: separated from Attribute specifically to make the intrinsic/extrinsic distinction explicit and enforce immutability by construction rather than by convention.

Relationships. Interfaces: Comparable, Serializable. Collaborators: Entity, Validator.

Attributes. | Attribute | Type | Explanation | |---|---|---| | name | str | Property name, unique within its owning Entity. | | value | PropertyValue | The immutable value. | | value_type | type | Declared type for validation. | | unit | str \| None | Optional unit-of-measure label. | | validator | Validator \| None | Optional validation rule. | | description | str \| None | Human-readable documentation string. |

Behaviors. create(), validate(), compare(), serialize()/deserialize().

Methods. - create(name, value, *, unit=None, validator=None) -> Property — Preconditions: name matches identifier grammar. Postconditions: value frozen. Exceptions: ValidationFailedError if validator rejects value at construction. - validate() -> ValidationResult — re-runs validator against value (useful after a schema change). - compare(other: Property) -> ComparisonResult — compares name + value.

Lifecycle. Immutable value object — no lifecycle machine (a new Property replaces an old one; Entity.add_property/remove_property manage that, not Property itself).

Events. None dedicated (covered by Entity's PropertyAdded/PropertyRemoved).

Exceptions. PropertyError (base) → ValidationFailedError, ImmutablePropertyError (raised on any attempted post-construction mutation).

Validation. name non-empty and unique per owner (uniqueness enforced by Entity, not Property); value must satisfy validator if present.

Serialization. Flat value; trivial across all formats.

Thread Safety. Immutable → thread-safe.


Attribute

General. Named, typed, mutable characteristic captured inside State snapshots. Design rationale: unlike Property, Attribute tracks a change history and participates directly in State diffing.

Relationships. Interfaces: Comparable, Observable, Serializable. Collaborators: Entity, State, Validator, Event.

Attributes. | Attribute | Type | Explanation | |---|---|---| | name | str | Unique within owning Entity. | | value | AttributeValue | Current value (mutable via set_value, never in place — see Methods). | | value_type | type | Declared type. | | unit | str \| None | Optional unit label. | | validator | Validator \| None | Optional validation rule, re-run on every mutation. | | history | list[tuple[Time, AttributeValue]] | Change log. |

Behaviors. create(), set_value(), validate(), compare(), observe(), serialize()/deserialize().

Methods. - set_value(new_value: AttributeValue) -> None — Preconditions: new_value passes validator. Postconditions: value updated; (Time.now(), new_value) appended to history; emits AttributeChanged. Exceptions: ValidationFailedError. - observe() -> Observation — snapshot read without mutation. - compare(other: Attribute) -> ComparisonResult — compares name + current value.

Private Helpers. _append_history(value), _trim_history() (bounded by constants.MAX_ATTRIBUTE_HISTORY_LENGTH).

Lifecycle. No independent lifecycle; lives and dies with its owning Entity.

Events. AttributeChanged, ValidationFailed.

Exceptions. AttributeError (base, note: shadows Python builtin name — SDK will alias as QmAttributeError internally) → ValidationFailedError, AttributeNotFoundError (raised by Entity).

Validation. Same as Property but re-checked on every set_value(), not just at construction.

Serialization. Flat value plus optional history (history inclusion is a serializer option — full history is often omitted from wire format and reconstructed from Entity.history instead, to avoid duplication).

Thread Safety. Not thread-safe — set_value() mutates in place; same single-writer caveat as Entity.update_state().


Behaviour

General. Declares which Interaction types an Entity may participate in, and under what Constraints, plus pre/post hooks. Design rationale: keeps "what am I allowed to do" declarative and inspectable, separate from Interaction's "what actually happened".

Relationships. Interfaces: Serializable. Collaborators: Entity, Interaction, Constraint, Event.

Attributes. | Attribute | Type | Explanation | |---|---|---| | permitted_interaction_types | set[str] | Whitelist of Interaction.type values. | | preconditions | list[Constraint] | Must hold before participation is allowed. | | pre_hooks | list[EventHandler] | Invoked before an Interaction is applied. | | post_hooks | list[EventHandler] | Invoked after an Interaction commits. |

Behaviors. create(), permits(), add_hook(), remove_hook().

Methods. - permits(interaction_type: str) -> bool — pure predicate, never raises. - check_preconditions(state: State) -> ValidationResult — evaluates preconditions against a candidate state. - add_hook(when: Literal["pre","post"], handler: EventHandler) -> None.

Lifecycle. No independent lifecycle; attached to an Entity at initialize() time and generally immutable thereafter (hooks may be added while INACTIVE, not while ACTIVE, to avoid races — enforced by Entity, not Behaviour).

Events. None dedicated.

Exceptions. BehaviourError (base) → InteractionNotPermittedError.

Validation. permitted_interaction_types entries must be non-empty strings; duplicate hook registration is a no-op, not an error.

Serialization. Hooks (callables) are not serialized — only permitted_interaction_types and preconditions are part of the wire format; hooks are re-attached programmatically on deserialization by the owning domain package.

Thread Safety. Read-mostly after attachment; safe for concurrent permits() calls.


Relationship

General. Typed, directed or undirected connection between two or more Entities within a System. Design rationale: kept generic enough to represent bonds, forces, correlations, or business associations without a combinatorial explosion of Relationship subclasses.

Relationships (meta). Interfaces: Identifiable, Serializable, Comparable. Collaborators: Entity, System.

Attributes. | Attribute | Type | Explanation | |---|---|---| | id | RelationshipId | Unique id. | | type | str | Domain-qualified kind (e.g. "chemistry.covalent_bond"). | | participants | list[EntityId] | Ordered if direction == DIRECTED. | | direction | RelationshipDirection (enum) | DIRECTED | UNDIRECTED. | | cardinality | RelationshipCardinality (enum) | ONE_TO_ONE | ONE_TO_MANY | MANY_TO_MANY. | | strength | float \| None | Optional weight/strength metric. | | metadata | dict[str, Any] | Free-form extension data. |

Behaviors. create(), involves(), other_end(), compare(), serialize()/deserialize().

Methods. - involves(entity_id: EntityId) -> bool — membership predicate. - other_end(entity_id: EntityId) -> list[EntityId] — Preconditions: entity_id in participants. Returns the remaining participant(s). Exceptions: EntityNotFoundError if not a participant.

Lifecycle. No independent lifecycle; created/destroyed via System.add_relationship/remove_relationship.

Events. None dedicated (covered by System's RelationshipAdded/RelationshipRemoved).

Exceptions. RelationshipError (base) → EntityNotFoundError, InvalidCardinalityError (raised if participants count violates cardinality).

Validation. len(participants) >= 2; count consistent with cardinality.

Serialization. Participants serialize as id references, never embedded copies (avoids duplication when the same Entity appears in many Relationships).

Thread Safety. Effectively immutable after creation (mutation = remove + re-add via System).


Constraint

General. A rule restricting the valid State space of an Entity/System. Design rationale: expressed as an evaluatable predicate object rather than inline code, so Constraints can be introspected, serialized, and (in later releases) fed to an external solver.

Relationships. Interfaces: Identifiable, Serializable. Collaborators: State, Entity, System.

Attributes. | Attribute | Type | Explanation | |---|---|---| | id | ConstraintId | Unique id. | | name | str | Human-readable name. | | expression | ConstraintExpression | Structured predicate descriptor (not raw code — see types.py). | | severity | ConstraintSeverity (enum) | HARD | SOFT. | | description | str \| None | Documentation string. |

Behaviors. create(), evaluate(), serialize()/deserialize().

Methods. - evaluate(state: State) -> ConstraintResult — Preconditions: none. Return: ConstraintResult{satisfied: bool, detail: str}. Never raises for an unmet Constraint — the caller (Entity.update_state()) decides whether to raise ConstraintViolationError based on severity.

Lifecycle. No independent lifecycle.

Events. None dedicated (covered by ConstraintViolated, emitted by the evaluating owner).

Exceptions. ConstraintError (base) → InvalidConstraintExpressionError (malformed expression at construction time).

Validation. expression must reference only Attribute/Property names declared on the target Entity type (soft-checked; hard enforcement is a domain-package concern).

Serialization. expression must be representable as data (no embedded closures/lambdas) precisely so it can cross the language boundary to future C++/Rust/Java/Go/Julia ports.

Thread Safety. Immutable after creation → thread-safe; evaluate() is a pure function of the input state.


Lifecycle

General. The ordered set of stages an Entity/System passes through, and the state-machine contract governing valid transitions. Design rationale: extracted into its own class (rather than inlined enum + if/else in Entity) so the transition table is declarative, testable in isolation, and reusable by System and future domain classes without duplication.

Relationships. Interfaces: Serializable. Collaborators: Entity, System, Event.

Attributes. | Attribute | Type | Explanation | |---|---|---| | stage | LifecycleStage (enum) | Current stage. | | history | list[tuple[Time, LifecycleStage]] | Full transition log. | | transition_table | dict[LifecycleStage, set[LifecycleStage]] | Allowed from -> {to...} transitions. |

Behaviors. create(), transition_to(), can_transition_to().

Methods. - transition_to(target: LifecycleStage) -> None — Preconditions: target in transition_table[stage]. Postconditions: stage = target; entry appended to history; emits a stage-specific Event (e.g. EntityActivated). Exceptions: InvalidLifecycleTransitionError. - can_transition_to(target: LifecycleStage) -> bool — pure predicate, never raises.

Canonical stage sequence (see entity.md/system.md §Lifecycle):

CREATED → INITIALIZED → ACTIVE ⇄ INACTIVE
                          ↓
                       SUSPENDED → ACTIVE
                          ↓
                       DESTROYED (terminal)

Events. EntityCreated/SystemCreated and every other *Activated/*Deactivated/*Suspended/*Destroyed event listed under Entity/System originate from Lifecycle.transition_to() internally, parametrized by which owner type is transitioning.

Exceptions. LifecycleError (base) → InvalidLifecycleTransitionError.

Validation. transition_table must not contain unreachable stages or a path out of DESTROYED (terminal-state invariant, checked at Lifecycle construction/registration time).

Serialization. history is fully serializable; transition_table is typically a class-level constant, not re-serialized per instance.

Thread Safety. transition_to() must be treated as a critical section — same single-writer caveat as Entity.update_state().


Event

General. A discrete, timestamped record of something happening, published through an EventBus. Design rationale: every other class in foundation emits Events rather than calling subscriber-callback lists directly, decoupling producers from consumers (logging, telemetry, domain listeners) entirely.

Relationships. Interfaces: Identifiable, Serializable, Timestamped. Collaborators: every other foundation class (as producers), runtime/telemetry/logging (as consumers, outside foundation).

Attributes. | Attribute | Type | Explanation | |---|---|---| | id | EventId | Unique id. | | type | EventType (enum) | Kind of event (see 07-events.md). | | source_id | EntityId \| SystemId \| InteractionId | Id of the object that emitted it. | | timestamp | Time | Instant of occurrence. | | payload | dict[str, Any] | Event-specific structured data. |

Behaviors. create(), serialize()/deserialize().

EventBus contract (companion, not a separate module): - publish(event: Event) -> None - subscribe(event_type: EventType, handler: EventHandler) -> SubscriptionId - unsubscribe(subscription_id: SubscriptionId) -> None

Methods. - create(type_, source_id, payload) -> Event — Preconditions: type_ is a registered EventType. Postconditions: timestamp = Time.now(). Never raises under normal use.

Lifecycle. Immutable, fire-and-forget value object — no lifecycle machine.

Events. N/A (an Event does not itself emit Events).

Exceptions. EventError (base) → UnknownEventTypeError, EventBusUnavailableError (raised by EventBus.publish if no bus is configured — publishing is best-effort and must never crash the producing operation; see Thread Safety note).

Validation. payload keys must match the schema declared for type_ in enums.py/types.py (soft validation, warning-level by default to avoid breaking producers on schema evolution).

Serialization. Fully flat, trivial across all formats — this is the natural format for audit logs and event-sourced replay.

Thread Safety. EventBus.publish() must be safe to call from any thread/task without blocking the producer on slow subscribers — architecture mandates an async-dispatch or bounded-queue contract at the EventBus implementation level (implementation deferred to runtime).

Metrics. events_published_total (by type), event_bus_queue_depth (gauge), event_dispatch_duration (histogram).


Observation

General. A recorded measurement of State at a point in Space and Time. Design rationale: separated from State itself because an Observation carries measurement semantics (observer, method, uncertainty) that a raw State snapshot does not.

Relationships. Interfaces: Identifiable, Serializable, Timestamped. Collaborators: Entity, System, State, Space, Time, Knowledge (consumer).

Attributes. | Attribute | Type | Explanation | |---|---|---| | id | ObservationId | Unique id. | | subject_id | EntityId \| SystemId | What was observed. | | state | State | The measured State (may be a partial projection, not necessarily the full State). | | observer | str \| None | Identifier of the observing agent/service. | | method | ObservationMethod (enum) | How the measurement was taken (e.g. DIRECT, DERIVED, SIMULATED). | | uncertainty | float \| None | Optional measurement uncertainty metric. | | timestamp | Time | Instant of observation. |

Behaviors. create(), serialize()/deserialize().

Methods. - create(subject_id, state, *, observer=None, method=ObservationMethod.DIRECT, uncertainty=None) -> Observation — never raises under normal use; emits ObservationRecorded.

Lifecycle. Immutable value object — no lifecycle machine.

Events. ObservationRecorded.

Exceptions. ObservationError (base) → InvalidObservationError (e.g. state inconsistent with subject_id's declared type).

Validation. uncertainty, if present, must be >= 0.

Serialization. Straightforward; embeds or references its state depending on serializer policy (embed by default, since Observations are often exported standalone for analysis).

Thread Safety. Immutable → thread-safe.


Knowledge

General. Structured information derived from one or more Observations, usable for Prediction. Design rationale: kept deliberately thin in foundation — it is a container and provenance tracker for models/rules/statistics, not a machine-learning engine (that lives in the ai domain package).

Relationships. Interfaces: Identifiable, Serializable. Collaborators: Observation, future ai/math packages (as producers of the actual payload).

Attributes. | Attribute | Type | Explanation | |---|---|---| | id | KnowledgeId | Unique id. | | subject_type | str | Domain type this Knowledge applies to (e.g. "finance.Instrument"). | | payload | dict[str, Any] | The structured model/rule/statistic itself (opaque to foundation). | | contributing_observations | list[ObservationId] | Provenance references. | | confidence | float \| None | Optional confidence score in [0, 1]. | | created_at | Time | Derivation instant. |

Behaviors. create(), predict() (interface point only — raises NotImplementedError in foundation; concrete Prediction logic belongs to domain/ai packages), serialize()/deserialize().

Methods. - create(subject_type, payload, contributing_observations, *, confidence=None) -> Knowledge — Preconditions: contributing_observations non-empty (Knowledge must be traceable to at least one Observation) unless explicitly marked metadata["a_priori"] = True. - predict(input_: Any) -> Any — architecture-only interface point; foundation's implementation raises NotImplementedError, documenting the contract signature for ai/domain packages to override.

Lifecycle. Immutable value object once created (a new derivation produces a new Knowledge, preserving the provenance chain) — no lifecycle machine.

Events. KnowledgeGenerated.

Exceptions. KnowledgeError (base) → InsufficientEvidenceError (raised if contributing_observations is empty and not marked a priori), PredictionNotSupportedError (raised by the default predict()).

Validation. confidence, if present, in [0.0, 1.0].

Serialization. payload serialization depends on its internal shape and is delegated to a format-aware Serializer; provenance references serialize as ids only.

Thread Safety. Immutable → thread-safe.


Transformation

General. A mapping that converts an Entity/State/System from one representation or form to another. Design rationale: the only extension point in foundation explicitly designed to be subclassed with real computational logic by domain packages — Interaction delegates all actual "what happens" logic to a Transformation.

Relationships. Interfaces: Serializable. Collaborators: Interaction, State.

Attributes. | Attribute | Type | Explanation | |---|---|---| | type | str | Domain-qualified kind (e.g. "quantum.hadamard_gate"). | | parameters | dict[str, Any] | Data-only parameters (no closures) needed to apply the mapping. | | input_schema | TypeSchema \| None | Optional declared input shape. | | output_schema | TypeSchema \| None | Optional declared output shape. | | is_reversible | bool | Whether inverse() is supported. |

Behaviors. apply() (abstract — domain packages implement), inverse() (optional, only if is_reversible), compose(), serialize()/deserialize().

Methods. - apply(state: State) -> State — abstract method; foundation defines only the contract (raise NotImplementedError), never scientific logic, per project constraints. - inverse() -> Transformation — Preconditions: is_reversible. Exceptions: TransformationNotReversibleError. - compose(other: Transformation) -> Transformation — returns a new Transformation representing self followed by other (a CompositeTransformation, still data-only at the parameters level where possible).

Lifecycle. Stateless/immutable value object — no lifecycle machine.

Events. None dedicated (covered by Interaction's events, since Transformation.apply() is always invoked from within Interaction.apply()).

Exceptions. TransformationError (base) → TransformationNotReversibleError, SchemaMismatchError (raised if input State doesn't match input_schema, when declared).

Validation. If input_schema/output_schema are declared, apply() implementations are expected (by contract, not by enforced runtime check in foundation) to validate against them.

Serialization. parameters must be plain data; the behavior of apply() itself is identified by type (a registry lookup at deserialize time resolves type back to a concrete class in the receiving process/language) — this is what makes Transformation portable across the future multi-language SDK ports.

Thread Safety. apply() is expected to be a pure function of its input State and parameters — no hidden mutable state — making it trivially safe for concurrent/parallel Interaction pipelines.


Space

General. The coordinate/topological context in which Entities and their State are situated. Design rationale: kept abstract enough to cover physical space, Hilbert space, feature space, or portfolio space under one contract, so visualization/simulation can reason about "where" generically.

Relationships. Interfaces: Serializable, Comparable. Collaborators: State, Observation, Entity, System.

Attributes. | Attribute | Type | Explanation | |---|---|---| | name | str | Human-readable identifier (e.g. "3D Euclidean", "2-qubit Hilbert space"). | | dimensionality | int | Number of coordinate axes/basis vectors. | | coordinate_system | str | Descriptive label (e.g. "cartesian", "computational_basis"). | | metric | str \| None | Optional metric/distance-function label. | | bounds | tuple[Any, Any] \| None | Optional min/max per-axis bounds. |

Behaviors. create(), contains(), distance() (interface point only), compare().

Methods. - contains(point: Any) -> bool — Preconditions: point matches dimensionality. Returns whether point lies within bounds (if declared; else always True). - distance(a: Any, b: Any) -> float — architecture-only interface point (raise NotImplementedError in foundation; concrete metric math belongs to math).

Lifecycle. Stateless/immutable value object — no lifecycle machine.

Events. None dedicated.

Exceptions. SpaceError (base) → DimensionMismatchError, OutOfBoundsError, MetricNotImplementedError (default distance()).

Validation. dimensionality >= 1; bounds, if present, must have matching arity.

Serialization. Flat value; trivial.

Thread Safety. Immutable → thread-safe.


Time

General. The ordering dimension over which State evolves, supporting both discrete (simulation timestep) and continuous (wall-clock) representations under one contract.

Relationships. Interfaces: Comparable, Serializable. Collaborators: every timestamped class (Entity, State, Event, Observation, Interaction).

Attributes. | Attribute | Type | Explanation | |---|---|---| | mode | TimeMode (enum) | WALL_CLOCK | DISCRETE_STEP | LOGICAL. | | value | float \| int | Numeric instant (seconds since epoch, step index, or logical counter depending on mode). | | unit | str \| None | Optional unit label for DISCRETE_STEP (e.g. "fs", "iteration"). |

Behaviors. now() (factory, WALL_CLOCK mode), create(), compare(), delta(), advance().

Methods. - now() -> Time — classmethod; wall-clock factory. - compare(other: Time) -> ComparisonResult — Preconditions: same mode (mixing modes requires explicit conversion). Exceptions: IncompatibleTimeModeError. - delta(other: Time) -> float | int — signed difference; same preconditions as compare. - advance(by: float | int) -> Time — returns a new Time instance (immutable) advanced by by.

Lifecycle. Immutable value object — no lifecycle machine.

Events. None dedicated.

Exceptions. TimeError (base) → IncompatibleTimeModeError.

Validation. value must be finite (no NaN/inf for WALL_CLOCK); DISCRETE_STEP value must be a non-negative integer.

Serialization. Flat value; trivial. WALL_CLOCK recommended wire format: ISO-8601 string for cross-language portability.

Thread Safety. Immutable → thread-safe.


Cross-Cutting Note on Attribute-Naming Collisions

Attribute as a class name shadows Python's built-in exception-related naming conventions in some tooling (AttributeError is a Python builtin). The exception hierarchy in 06-exceptions-and-utilities.md therefore aliases the foundation AttributeError as QmAttributeError internally to avoid confusing shadowing, while the class Attribute itself is unaffected since it does not collide with any builtin.