Skip to content

22. Dispatcher

Context

A request says what faculty it needs; a model name does not say whether that faculty exists, accepts the necessary material, or is ready now. Local Soulstones and remote Portals change state independently of the request. LychD needs one admission office that can bind semantic demand to a live capability without acquiring the right to move the machine.

Decision

Dispatcher selects a capability and admits a temporary grant. It never starts, stops, loads, or evicts an Animator. Orchestrator owns those transitions; Graph owns whether a wait stays live or becomes durable.

The accepted general-service boundary is deliberately narrow:

typed demand → interface/profile declaration → fresh state → exact WARM grant → registered lease

A capability-bearing Animator is the route; there is no semantic provider-pair abstraction. Dispatcher emits its authoritative dispatch event only after lease admission. The delivered source implements the narrower v1 chat-model and toolset compatibility path described below; the general-service records and discriminated grant union remain Designed.

The live Animator is a capability/runtime handle, not a deployment document. It carries its exact Rune identity, Connector, and typed runtime surfaces while Containers, Bind, and Scribe retain physical compilation authority.

Capability Binding Cartography

The binding keeps five questions explicit: what was requested, what is declared, what was tested, what was observed, and what may be used now.

Record Office
CapabilityDemand@1 Exact interface, operation, typed material, feature facts, invocation mode, and policy constraints required now.
CapabilitySpecV2 Immutable declaration of Animator, interface, exact profile, operations, invocation mode, driver, and route shape.
CapabilityEvidenceRef Reference to exact producer-attributed conformance evidence; neither admission nor current readiness by itself.
CapabilityState Latest observation for that exact declared binding and, when required, operation.
discriminated capability grant Temporary, WARM binding exposing only the callable surface for one admitted operation.

Designed v2 keys are {animator}:{interface_id}:{profile_ref} where profile_ref includes a safe stable id and exact immutable revision or content digest. They cross registry, Dispatcher, Orchestrator, events, Workers, and Graph; after a pause a consumer re-fetches this canonical identity, never a mutable handle. The operation remains explicit in demand and grant rather than being hidden in profile identity.

Every component uses the closed slug grammar owned by Capabilities; colons, paths, whitespace, controls, and floating revisions are forbidden.

Delivered v1 compatibility projection

Current CapabilitySpec keys are {animator}:{family}:{model_id} and the single CapabilityGrant class contains either an admitted Pydantic AI chat model with explicitly declared agent-loop toolsets, or a non-empty tool_execution toolset surface. The closed families are chat, vision, embedding, stt, tts, tool_execution, and rerank; model_id remains required. Only chat and non-empty tool_execution can issue this v1 grant. The other family labels remain routing metadata and fail closed at issue; they do not prove an executable STT/TTS call, visual or embedding job, rerank call, Scout effect, host tool, or live session merely because the vocabulary can name one.

Current [[models]] declarations may project into v2 only through an explicit compatibility map from an exact family/model pair to an exact interface/profile. Persisted v1 keys are never silently reinterpreted and an unknown family never becomes a generic service.

Interfaces, profiles, and material

An interface is the versioned semantic ABI, such as model.chat@1, echo.transcribe@1, prism.image@2, or prism.scanner@1. A profile pins the exact model, weights, graph, workflow, configuration, dependencies, licenses, formats, languages, limits, and conformance evidence that implement it. Operations name admitted acts such as generate, edit, transcribe, segment, or search.

A demand matches only its exact interface and operation, typed I/O contract references, every required material facet and feature fact, invocation mode, and eligible profile. Unknown support never admits work. Image-capable chat remains chat; speech emitted by a chat model is not thereby an eligible synthesizer; tool is never a universal effect interface.

Declaration and observation

The v2 declaration holds animator/runtime/source kind, interface/profile, operations, typed I/O, invocation mode, driver/dialect, feature facts, conformance evidence, resource envelope, dynamic trait, and concurrency intent. The current v1 declaration retains family/model, input modalities, tool support, generation, and context fields; connector-local model summaries select Chat or Responses. The v1 dispatch demand cannot require streaming support or output modalities. is_dynamic says a running local runtime may load a model; it is not readiness. The observed phase is COLD, ACTIVATABLE, WARMING, WARM, ERROR, or UNKNOWN.

Soulstone declarations compile from Runes and selected adapters; probes update state and may fill explicitly open runtime facts. An unknown local runtime stays passive unless an explicit adapter and dialect profile give it semantics. "OpenAI-compatible" without a named, proved dialect gives it none. Generic command planning synthesizes no Connector, model catalogue, or readiness. The supported llama.cpp profile disables engine idle sleep: healthy fixed-runtime inventory can survive sleep, and router autoload=false does not prevent a sleeping child from waking during inference. Managed argument vectors begin with --sleep-idle-seconds -1; extra_args cannot contain another sleep control. Passthrough vectors must begin with that exact pair, either at the start for an image entrypoint or immediately after a non-option executable token. Later sleep controls, underscore aliases, and equals spellings are refused. Admitted passthrough bytes remain unchanged. Arbitrary wrappers that cannot honor this flag are outside this profile. This is command admission, not attestation of a running engine, external mutation, or a shared endpoint's actual configuration. Unregistered OpenAI-shaped aliases fail Soulstone capability-coverage validation before Bind or registry publication. A Portal creates routes only for capabilities declared in its Rune; zero declarations mean zero routes. Portal routes are non-dynamic and non-dedicated. Their default probe = false makes no discovery egress, and a readiness probe never authorizes private transmission. That unprobed route is projected as UNKNOWN and unverified, never fabricated as WARM, so it cannot receive a grant.

OpenAI-shaped transport construction binds only the declared endpoint and API credential. It does not retain ambient SDK organization, project, webhook, admin credential, or custom-header configuration. Environment headers cannot override the declared credential or reach another endpoint. Model, readiness, and local control-plane HTTP clients ignore ambient proxy configuration and do not follow redirects. Model transport performs one physical transmission per invocation: implicit SDK retries are disabled, including after a lost acknowledgement. Agent validation repair remains an explicit model round under its existing budget; this transport rule authorizes neither another provider nor blind replay of an ambiguous effect. Model-client close and re-entry retain the same transport policy.

Runtime hydration

Soulstone adapter ownership is the adapter's exact declared runtime key; registration cannot claim another adapter's runtime. Registry hydration is staged and rejects a runtime unless it retains the exact input Rune and its name equals the Rune name. Every synthesized specification must then name that Animator, the Rune's canonical runtime, source kind and concurrency intent, and the canonical {animator}:{family}:{model_id} key before any snapshot is published. An adapter constructs its runtime catalogue once per hydration. Capability synthesis consumes that runtime's captured model summaries and generation defaults instead of re-reading a preset or independently rebuilding the connector's catalogue. Bind uses the same construction boundary to check capability coverage without probing or activating the runtime. A model-local request policy is validated against the resolved runtime, exact model and Chat surface during synthesis, before a registry snapshot is published. Its generation value is frozen into the specification and grant; issuance must not reopen the Rune or recover settings from model names. The hydrated model must retain the admitted Chat transport before the policy can become outbound settings. A v1 Run still pins its capability key, not a durable declaration-generation fingerprint: this policy grants neither live reconfiguration nor policy continuity across arbitrary operator changes. Construct and qualify an immutable assembly for fresh admissions.

Endpoint hydration preserves which fields the operator supplied: allocating a port must not turn omitted managed-command defaults into declarations that conflict with an explicit exec command.

Probe completeness and invalidation

A probe is a total observation of the exact requested capability-key and operation set. Duplicate, missing, or foreign keys are contract failures; a successful result replaces that Animator's cached states as one operation, while an exception, cancellation during the probe, or malformed result invalidates the affected prior observations. A failed full-registry refresh invalidates the full cached observation set rather than preserving stale warmth. Initial hydration probes the complete staged snapshot and publishes neither runtime nor state until every result validates, so a failed first probe leaves the registry retryable. Portal egress probing is selected only by the exact Portal definition's typed strategy.

Resolution

Candidate selection

Designed resolution receives CapabilityDemand@1, run and station-attempt identity, deadline, and priority. It removes ineligible declarations and orders the rest deterministically under an admitted selection policy. This is readiness matching, not a judgment of quality, price, privacy, or correctness.

Current lease_grant retains its narrower family/model/modalities/tools signature, an optional exact capability_key constraint, and deterministic open/non-error/active/warm/name/key order. Cached observations rank eligible routes; they are not permanent denial. The exact key intersects every other demand constraint; it cannot bypass readiness, drain admission, or Portal quarantine. A model alias alone still denotes an eligible pool. An unknown, incompatible, or unavailable exact key never substitutes another candidate.

Bridge preference at admission

The bounded revision-2 Bridge preference ranks only the two configured literal registry keys before a fresh Run is admitted. Validate both complete declarations before probing: each must have SourceKind.SOULSTONE, family CHAT and supports_tools = true. Zero-weight routes are disabled, not exempt from declaration validation.

For positive-weight routes, perform one successful direct refresh_capability_states_for_animator batch per distinct Animator and consume its returned immutable states. A complete Animator batch may include a zero-weight capability sharing that Animator; its phase/time are omitted from the retained decision and it remains ineligible. Never substitute cached observations through a refresh-or-cached helper. The complete probe batch has one fixed five-second cooperative timeout and no retry. Timeout, cancellation, malformed observations or operational probe failure abort admission with content-free public refusal codes; an earlier successful route is not selected after a later probe fails. Existing adapters retain ownership of probe cancellation and cleanup. Five seconds does not establish a hard process, network or cleanup containment limit.

After all probe awaits, recheck that the validated declarations are unchanged and read current lease admission. A changed declaration aborts admission. Capture each Animator's admission generation before its probe; a changed generation at the final check excludes its routes with the safe readiness_changed code, even if admission has reopened. Only a positive-weight route with a successfully observed WARM phase and currently OPEN admission is eligible. Other successfully observed phases and closed admission are bounded exclusions. Select the greatest weight among eligible routes, breaking a tie by ascending canonical key; an empty eligible set refuses admission without activation, grant preparation or model entry.

Retain server-observed times and the bounded observations under Configuration's custody law. The batch is not an atomic cross-Animator readiness snapshot or a reservation. It cannot promise that the selected route will remain ready through persistence, queueing or execution. Existing exact-key Dispatcher issue and generation checks still apply; execution may enter Stasis or refuse, and never substitutes the other preference route. This policy changes neither grant, provider-entry, cleanup nor Bridge call/tool/consent/token-budget semantics.

Refresh the selected route

An invalidated or absent cached observation ranks as UNKNOWN, so the selected declaration can be freshly observed on a later request. It never recovers cached warmth. An eligible cached ERROR ranks behind other candidates with the same admission state and is freshly observed when selected. Recovery therefore needs no unrelated transition or polling service. A fresh ERROR still refuses the request without grant or hardware transition; v1 does not retry another candidate after its chosen route fails.

The chosen record is refreshed immediately before issue. Capability specifications, observations, generation profiles, and model summaries are frozen value objects with immutable nested fields. Registry and adapter projections may share those values; ordinary consumer mutation cannot rewrite the canonical declaration. Animator Runes freeze group membership and both explicitly supplied and omitted environment/secret maps. Inventory collections use immutable sequences. These in-process value contracts are not a sandbox for extension code.

Observation Result
WARM, admission open, executable v1 surface admitted Issue and register a grant.
WARM, metadata-only or empty v1 surface CapabilityUnavailable.
COLD/ACTIVATABLE/WARMING, dedicated HardwareTransitionRequired.
Those phases, shared or non-managed CapabilityUnavailable.
ERROR Unavailable with observed reason.
UNKNOWN Re-probe; unavailable if still unresolved.
Drain closes during issue Transition required, and only this race is translated.

Refusal and transition signals

Known metadata-only v1 families are refused before readiness probing or a drain wait: hardware convergence cannot create their missing executable surface. The final registry issue probe can observe a different phase from Dispatcher's earlier probe; its typed readiness failure returns that exact observation to the same phase decision table. Hydration, policy, and malformed-probe failures remain ordinary failures and never become hardware requests merely because issue failed.

The transition signal contains capability key, Animator name, and optional ready estimate—no connector, model, lease, or service handle. Graph may release its edge, ask Orchestrator to converge, and dispatch afresh. Dispatcher never calls activate_capability or await_warm.

The Grant Lease Doctrine

Registry issue permits only a WARM record. The designed discriminated union exposes one of:

  • ModelGrant: a Pydantic AI model and admitted agent-loop toolsets;
  • CallGrant: one typed bounded call driver;
  • JobGrant: typed submit, status, cancellation, result, and reconciliation surfaces; or
  • SessionGrant: one typed, bounded, epoch-fenced live-session driver.

Every variant contains the exact specification, operation, warm observation, lease identity, holder, issue time, scope, and only the live surface it admits.

Current source delivers one frozen v1 compatibility grant: chat must hydrate its Pydantic AI model and receives agent-loop toolsets only when supports_tools = true; tool_execution must hydrate at least one toolset and receives no model. All other v1 families fail closed because no typed executable surface exists. The grant does not expose its Animator or Connector. Specifications and observations share immutable nested values; only explicitly admitted model or toolset handles remain live process objects.

The registry creates the identity; Dispatcher registers it in the process-local LeaseLedger. Its context manager releases on ordinary exit, body failure, and observation failure. Duplicate ids are defects. A closed drain gate or fresh dedicated COLD/ACTIVATABLE/WARMING observation turns into Stasis; unknown, erroneous, shared, or non-readiness issue failures do not. The same scope owns the model's provider context. It closes the provider before releasing the lease, including caller cancellation and refusal during admission; provider-profile adapters forward this lifecycle to their transport. A cached, model-free Agent owns no grant client.

Drain truth is absence of ledger leases on an Animator. Admission closes before drain waits, so new work cannot enter an eviction set. A completed drain and reopen also invalidates grant preparation that began before that drain. Dispatcher captures the Animator's process-local admission generation before asynchronous grant issue or provider entry; the ledger checks that generation and open admission together before registering the lease. A changed generation returns the same handle-free hardware-wait signal after provider cleanup, without publishing a dispatch event. Unrelated Animators retain their own admission generations. A run waiting for its remedy owns no lease. expires_at is recorded but unenforced: there is no renewal or distributed stale-grant fence. Grants contain trusted live objects and may not enter Graph state, persistence, or delegated processes.

From a grant to an enduring effect

For a bounded immediate call, the lease ends with that call. Before any local or remote asynchronous effect is first submitted, execution ownership must transfer to a persisted ServiceJobAttempt@1; attempts on managed resident or scarce local substrate additionally require an Orchestrator-visible exact reservation and fence. Live work uses its domain-owned bounded session record; no common SessionAttempt is accepted yet. A process-local lease alone cannot prevent duplicate remote effects or fence local residency. Workers (14) owns that Designed handoff.

Durable Content and ArtifactRef

The Designed content path requires Runs and checkpoints to name content with ArtifactRef, never embedded bytes: identity, sha256: digest, media type, byte size, and public/internal/private/restricted classification. Media-type projection to input modality for matching remains Designed.

Current code defines this immutable metadata shape and carries references in delegated-job requests and results. Ordinary Intent and Bridge admission remain text-only; they neither carry artifact references through the Run ledger nor infer required modalities from media types. Dispatcher filters explicitly supplied modality requirements. A separate, unregistered trusted-local custody adapter provides bounded retention under an injected current authorizer. Bridge artifact admission, credential-backed authority, and provider materialization remain separate work.

Explicit local embedding compatibility profile

An exact configured embedding family/model pair may additionally declare an immutable model.embed@1 profile. This does not change lease_grant: unprofiled embedding labels and embedding requests through the chat/tool grant path still refuse before readiness or host work. The separate lease_embedding entry requires the canonical capability key, profile digest and operation (embed_document or embed_query). It admits only local Soulstones, reuses fresh issue-time readiness, drain generations and lease accounting, and exposes only a bounded driver. Provider entry precedes lease registration; cleanup completes before release, including cancellation. A driver cannot execute outside its entered lease or execute another operation than its grant.

The first explicitly selected profile is one fixed Qwen3-Embedding-0.6B Q8_0 artifact and llama.cpp b9445 dialect, with last pooling, 1024 dimensions and L2 normalization. Its immutable manifest binds model/image/build digests, exact query/document rendering, tokenizer options, conformance receipt and input/output/deadline bounds. It accepts one input per call, at most 4096 rendered UTF-8 bytes and 511 runtime tokens. Oversize input refuses; truncation is forbidden. Runtime configuration selects a managed single-model command with no passthrough, router, environment, model or profile overrides. Configuration and a prior conformance receipt establish admission, not fresh attestation of a mutable externally replaced process or artifact.

A source-bearing call requires a current-authority callback immediately before tokenization and embedding transmission. Source eligibility and stale-completion fencing remain the caller's domain obligations; no database lock spans runtime work. The local dialect accepts only a literal loopback HTTP endpoint, disables ambient proxies and redirects, bounds raw response bytes before parsing, and validates indexed output identity, count, dimension, finite numbers, nonzero norm and usage. Returned execution values contain vectors and digests, not original text or raw provider responses. Errors expose safe codes. No Portal, general SpecV2, detached job, semantic recall, ANN or training operation is authorized by this compatibility profile.

The fixed profile requires an explicit port and literal-loopback base_url so it does not inherit hostname resolution or generic endpoint allocation. For example, a local Soulstone may select embedding_profile = "qwen3-06b-q8-b9445@1", port = 8080, base_url = "http://127.0.0.1:8080/v1", model_path = "/models/embedding.gguf", and quadlet.image = "ghcr.io/ggml-org/llama.cpp@sha256:39f4f2c5fd4537f85208f905e70269b5b691fbc6de00fd916c37a60a710b7d52". The operator supplies the public artifact through the existing mount configuration. Model path, image and profile are configured admission evidence: replacing a mounted file is not detected by health/inventory probes. The retained public manual fixture in tests/fixtures/embeddings/qwen3-06b-q8-b9445.json is dialect qualification, not fresh runtime attestation or proof of Orchestrator activation. Its laboratory RLIMIT and network isolation do not become general deployment resource guarantees.

The output scalar encoding is float32-le: provider numbers are explicitly projected to IEEE754 binary32, then checked for finite values, dimension 1024 and L2 norm within 1e-4, without renormalization. Vector digests hash the concatenated little-endian binary32 bytes. This same canonicalizer is required at a durable pgvector handoff and after reload; JSON decimal formatting is not the storage identity. Receipts bind vector digests and input/rendered digests without raw source text or duplicate vector coordinates.

A driver closes admission and joins or cancels its outstanding requests before Dispatcher releases the lease. Failing or cancelled driver entry owns its own partial cleanup; the fixed driver entry has no suspension. Safe refusal codes replace provider bodies and transport exception causes.

Policy boundary

Selection is mostly a future enforcement seam, not a complete policy engine. One coarse rule is delivered now: every SourceKind.PORTAL candidate fails closed before grant, including direct-key dispatch and direct registry issue, because no typed egress admission path exists. This quarantine is not privacy-safe Portal operation. Sigil visibility, Portal secure mode, Toll arbitration, Censor/Privacy Cut/TransformationReceipt/Egress Gate, unsupported-modality planning, A2A, and delegated-agent choice are unwired. They must compose one typed, fail-closed path before grant; none may create an alternate route around Dispatcher.

Security requires both admission before a Portal grant and a decision over the exact canonical payload immediately before transmission. Dispatcher will compose those decisions; it does not transform data or currently admit any Portal grant. A future path must not allow retry, fallback, delegated child, or consent to reuse an obsolete verdict.

Declaring and reading capabilities

Use Capabilities for declaration formats and the meaning of observed readiness. The guide relates these records to the capability an operation actually needs and distinguishes the current compatibility surface from the general-service design.

Consequences

Accepted

  • Requirements, observed readiness, usable runtime handles, and drain accounting are separate.
  • A hardware wait is handle-free and cannot deadlock on its own lease.
  • Local and remote routes share typed grant law without sharing lifecycle authority.

Cost

  • Registry and lease coordination are process-local.
  • Order ignores quality, price, privacy, and Sigil visibility; Portal sources are quarantined and incomplete probes can refuse a viable local route.
  • The matcher picks one candidate and does not score or explain alternatives.

Verification

Current Dispatcher unit tests cover v1 matching, deterministic preference, generation settings, and frozen compatibility grants. The current dispatch decision table covers every readiness row, grant/drain race, event ordering, and release on failure; registry, Portal, adapter, and lease tests cover v1 synthesis, fresh pre-issue probing, fail-closed family surfaces, opt-in probes, hydration, and drain accounting. The fixed embedding profile additionally has configured registry/Dispatcher HTTP integration tests for exact wire bytes, bounded responses, stale authority, cancellation and float32 identity, plus a retained manual dialect fixture. General interface/profile catalogs, grant variants, service attempts, and capability-set placement remain Designed. State of Work owns delivery claims.