mesh-llm-runtime-event-contracts 0.78.1

Dependency-leaf producer contracts for mesh-llm runtime events
Documentation

mesh-llm-runtime-event-contracts

Dependency-leaf producer contracts for mesh-llm runtime events.

This crate defines the pure data model that the runtime-event system's producers, ingress boundary, and reducer speak: fact families (what happened), the wire-shape carriers each field rides on, process-local identities, delivery classification, and the raw ABI mirror for events that originate in native code. It is deliberately dependency-free of every mesh-llm host/presentation crate — its only runtime dependency is uuid, so nothing that consumes these types can create a dependency cycle back into mesh-llm-host-runtime, mesh-llm-events, or the TUI. Downstream crates build the actual event bus, reducer, and SSE wire encoding on top of these contracts.

Event families

RuntimeFact is the top-level fact enum. Its 15 variants are each a FamilyFact<K> pairing a family-specific K "kind" enum with a shared FactData payload:

  • Lifecycle families (src/families/lifecycle.rs): NativeRuntimeEventKind, ModelPreparationEventKind, ModelLoadingEventKind, ModelAvailabilityEventKind, ModelUnloadingEventKind, StageTopologyEventKind, SessionEventKind.
  • Execution families (src/families/execution.rs): RequestEventKind, PrefillEventKind, GenerationEventKind, KvRuntimeStateEventKind, ResourceHealthEventKind, DiagnosticEventKind, NodeAvailabilityEventKind, EventSystemHealthEventKind.

Each kind enum is generated by the crate-local event_family! macro (src/lib.rs), which gives every variant a const ALL: &'static [Self] slice and an as_str() stable wire id (e.g. ModelLoadFailed -> "model_load_failed"). families::all_event_ids() concatenates every family's ids into the full 184-id catalog. FactData (src/fact_data.rs) carries the shared optional fields every fact may attach: ScopeIdentities (model/topology/stage/session/request/device ids), a StateTransition, a Progress, an Outcome, a ReasonCode (19 named codes plus a bounded Unknown(UnknownReasonCode) escape hatch), a Duration, and bounded NumericSummary / HumanSummary values, each length-capped (IDs at 256 bytes, state/summary keys smaller, human summaries at 512 bytes) so a producer cannot smuggle unbounded payloads through the contract.

Envelope and identity

RuntimeEventEnvelope (src/carrier.rs) is the outer wire shape: schema version, EventId, OperationScope, ProducerSource (Native / Rust / Reconciled), Severity, wall-clock and process-monotonic timestamps, an optional NativeSourceEnvelope, and the RuntimeFact payload. CarrierKind enumerates all 19 named carrier fields (schema version, category/kind, producer, severity, timestamps, sequence numbers, identities, state transition, progress, outcome/reason, duration, bounded summaries) and which of three locations — RuntimeEventEnvelope, NativeSourceEnvelope, or FamilyFact — owns each one. This is the field-placement contract the spec-pinned inventory below cross-checks.

src/identity.rs defines the process-local identity types: EventSequence is the sole reducer ordering key (native sequence numbers are source data only and carry no cross-process ordering or correlation promise); ProcessInstanceId, OperationId, and ChildOperationId are UUID newtypes minted by the uuid_identity! macro; and OperationScope distinguishes a root operation (Root(OperationId)) from a child scoped under a root (Child { root, child }). A request-root OperationId intentionally stores the same UUID bytes as the logging request ID so the two can be correlated without a mapping table.

Delivery classification

src/delivery/ classifies every fact into one of four DeliveryClass values — Terminal, StateTransition, Progress, or Diagnostic — via RuntimeFact::delivery_class(). The classification is a pure match over each family's kind enum (delivery/lifecycle.rs for the seven lifecycle families, delivery/execution.rs for the eight execution families); a caller never supplies a delivery class itself, so classification cannot drift per call site.

Ingress boundary

RuntimeEventIngress (src/ingress.rs) is the synchronous submission trait producers call: try_submit(&self, fact: RuntimeFact) -> SubmitOutcome. Implementations must return immediately without waiting for consumers. SubmitOutcome reports what happened to the fact — Accepted / Coalesced / DroppedProgress / DroppedDiagnostic / RejectedShuttingDown / RejectedCapacity / RejectedCancelled / TerminalDeliveryFailed — so a producer never blocks on downstream delivery.

Native ABI mirror

src/native.rs mirrors the raw C struct the patched llama.cpp staged runtime emits: NativeSourceEnvelope and its NativeEventCategory / NativeEventKind / NativeEmitter / NativeProgressUnit / NativeFailureCode / NativeStatus newtypes over the underlying integers. The numeric_summary_0..3 fields are Option<u64> specifically because their presence depends on the emitting native runtime's struct_size: None means the native runtime predates that field (or was built without it), not that the value is zero.

Spec-pinned inventory

inventory/runtime_events.toml is the crate's single normative catalog: carriers, reason codes, native callback kinds, delivery/privacy classes, projection profiles (and their allowed/forbidden projected keys), terminal- outcome mappings, "same fact" rulings, and one [[families]] entry per family (owning crates, source paths, producer symbols, authoritative outcome, reducer effect, privacy class, and the migration task that introduced it). It is pinned to a ratified spec by content hash: runtime_events.toml's spec_sha256 field must equal the live SHA-256 of .omo/specs/event-system.md, and tests/inventory_contract/metadata.rs fails the suite if the two drift apart. tests/inventory_contract/coverage.rs cross-checks the inventory's 137 spec-bullet expansions against inventory/spec_manifest.json — itself produced from the same spec section (8.1-8.15) by scripts/extract-runtime-event-spec-manifest.py — and adversarial.rs / projection.rs round out the contract with tampered-input and projection-key checks. scripts/generate-runtime-event-inventory.py mechanically regenerates the TypeScript mirror at fixtures/runtime_event_inventory.ts from the same TOML, so the amendment procedure recorded in the inventory itself ("change the inventory, generated manifest/docs/fixtures, exhaustive Rust tests, and schema-version review atomically") is the intended way to add or change an event. The generator requires Python 3.11 or newer and uses the standard-library TOML parser.

Fixtures

fixtures/runtime_events_v1/*.json (frames.json, cursors.json, recovery.json) pin the /api/runtime/events/v1 SSE wire contract: event names, required envelope/per-event keys, the projected-key allowlist, and cursor/replay-gap framing. These fixtures are consumed from two independent places so the contract cannot silently drift: mesh-llm-host-runtime's api::routes::runtime_events::runtime_event_api_tests round-trip test in Rust, and scripts/ci-windows-runtime-events-smoke.ps1, which drives a composed Windows product binary and asserts its live SSE frames against the same fixture files.

Dependencies

This is a dependency-leaf crate: its only runtime dependency is uuid (the v4 feature, for the identity newtypes). Dev-dependencies (serde, serde_json, sha2, toml) are used only by the tests/inventory_contract suite to parse and hash-verify the inventory and spec manifest. It has zero workspace-path dependencies by design, so nothing downstream — including mesh-llm-host-runtime and the presentation layers that will eventually consume these contracts — can create a cycle back into this crate.