Bus namespace migration¶
In a nutshell
The Formal Specifications renamed many legacy bus topics into the
ovos.* namespace. ovos-bus-client bridges old and new names automatically, so producers
and consumers can each switch at their own pace. The bridge is a pending removal. See the
warning below.
Namespace migration¶
The Formal Specifications rename many bus topics into
the ovos.* namespace, for example recognizer_loop:utterance →
ovos.utterance.handle and complete_intent_failure → ovos.intent.unmatched
(the full list is the legacy ↔ spec table).
A few families, such as the mycroft.skill.handler.* / ovos.intent.handler.*
trio, are deliberately not bridged. See that page's "Not bridged" note.
Renaming a topic across an
ecosystem of independently-released repos cannot happen in one coordinated step.
So ovos-bus-client migrates automatically and incrementally. The legacy
and the new names interoperate transparently while the ecosystem moves over.
The canonical legacy to spec topic map lives in the NamespaceTranslator from
ovos-spec-tools, and each MessageBusClient applies it in two
directions. On the receive side, an incoming message re-dispatches to local listeners
under the other spelling (modernize). On the emit side, since bus-client 2.8.3a1 a
canonical emit of any mapped topic also puts a real, marked legacy-spelled twin frame on
the wire (gated on emit_legacy; escape hatch OVOS_BUS_WIRE_LEGACY_TWINS env var /
websocket.wire_legacy_twins config key, default on, added in 2.8.4a1). Legacy-spelled
emits are not twinned — their canonical counterpart is delivered receive-side only. Intent
topics go through the separate intent bridge instead:
sequenceDiagram
participant Producer
participant Bus as ovos-messagebus
participant Client as BusClient<br/>(any process)
participant LegacyHandler as legacy topic<br/>handler
participant SpecHandler as ovos.* topic<br/>handler
Producer->>Bus: emit(recognizer_loop:utterance)
Bus->>Client: broadcast<br/>recognizer_loop:utterance
Client->>Client: on_message:<br/>NamespaceTranslator lookup
Client->>LegacyHandler: dispatch<br/>recognizer_loop:utterance
Client->>SpecHandler: locally re-dispatch<br/>ovos.utterance.handle
Diagram: a producer emits the legacy recognizer_loop:utterance topic, the bus broadcasts it unchanged, and each receiving MessageBusClient uses the NamespaceTranslator to dispatch it to both the legacy handler and, re-dispatched locally, the spec ovos.utterance.handle handler.
- A legacy-spelled or unmapped
emit()sends exactly one message over the websocket: the topic the caller chose. A canonical emit of a mapped topic sends two on current defaults: the canonical frame plus its marked legacy twin (see above). - When a frame arrives back over the websocket (to every connected client, including the
sender), each client's
on_messagehandler locally re-dispatches it under its counterpart topic(s) too, using the translator to reshape the payload where the shape changed. This re-dispatch is a listener-delivery convenience inside each process, not a wire frame. The broadcast server itself never creates counterpart frames; any twin on the wire came from the emitting client. - listen: subscribing to either name (
bus.on(...)) also delivers the counterpart, with de-duplication so a handler that would match both fires exactly once.
The result: a producer and a consumer can each switch from a legacy topic to its ovos.*
spec name in any order, with no coordination, without every component switching at once.
Turning the bridges off¶
Each direction is independently controllable (default true), via environment
variable or bus configuration:
| Direction | Env var | Config key | Effect |
|---|---|---|---|
| modernize | OVOS_BUS_MODERNIZE |
modernize |
a received legacy topic is also locally re-dispatched under its ovos.* counterpart |
| emit_legacy | OVOS_BUS_EMIT_LEGACY |
emit_legacy |
a received ovos.* topic is also locally re-dispatched under its legacy counterpart |
Because the bridging happens per-process on receive, turning a direction off only stops that
process from locally delivering the counterpart to its own handlers. A deployment whose
components all speak ovos.* can set emit_legacy=false once no local handler still needs
the legacy delivery, and disable modernize once no legacy producers remain.
modernize is required, not cosmetic, on a canonical-only skill container
A skill container that only registers ovos.* handlers is not automatically safe from an
older producer still emitting legacy topics. The wire frame it receives may be legacy-only.
That skill hears it at all because its own MessageBusClient (any ovos-bus-client
since 2.6.3a1, where the bridge became receive-side in commit 0f0a241, PR #258; before
that the counterpart was a second wire message, and 2.8.3a1 re-added marked wire twins
for canonical emits — see above)
re-dispatches the legacy arrival under its ovos.* counterpart on receive, per modernize
being on. Disabling modernize on such a container does not just drop a redundant delivery.
It silently stops that container from ever hearing a legacy producer again. Never disable
modernize on a skill container while any producer in the fleet may still be on legacy
topics. Treat the default true as the safe choice everywhere except a deployment that has
fully verified every producer speaks ovos.*.
Intent dispatch topics: the .intent suffix is gone¶
A related, separately-bridged rename: per-intent dispatch topics are now the canonical
<skill_id>:<intent_name> with no .intent suffix. Old ovos-workshop releases built
the topic from the Padatious resource filename, so the extension leaked onto the wire. A
skill with food.order.intent listened on <skill_id>:food.order.intent. Current workshop
registers <skill_id>:food.order (OVOS-MSG-1 §2.1.1).
ovos-bus-client 2.8 bridges the skew with two stateless rules: every canonical intent topic
emitted also goes out as its .intent-suffixed twin (marked as a twin, so nothing double-fires),
and every unmarked suffixed topic received is locally re-dispatched under its canonical
spelling. Config-gated, on by default. Test rigs get the same behavior from FakeBus as of the
ovos-utils alpha released 2026-08-14.
Two practical rules for authors:
- New code and tests should compute the canonical topic with
ovos_spec_tools.intent_topics.canonical_intent_topic(...), rather than hard-coding either spelling. Literal.intentlisteners are deprecated but not broken. - The spelling goes canonical when either side modernizes: the skill registers canonical
(workshop ≥ 9.3.11a2) or the matcher canonicalizes (padatious ≥ 2.0.1a1 strips the suffix at
registration. Adapt is unaffected: its intent names come from
IntentBuilder, never from filenames, so there was no suffix to strip). Pinning one side back does not restore the old spelling.
The bridge is scheduled for removal
ovos-bus-client#272 is an
open kill-switch pull request that deletes the bridge entirely: after it merges,
clients speak ovos.* spec topics and nothing else, and setting the modernize /
emit_legacy flags raises RuntimeError. Its stated merge condition is a fleet
already upgraded to spec topics. Migrate remote consumers to the spec names ahead of
it. See Upcoming Changes.
Bridged is not the same as conformant
ovos-test-harness asserts spec behavior on the
canonical ovos.* topics. A component becomes spec-conformant once it
speaks ovos.* directly. The bridge keeps it interoperable in the
meantime, but it does not make it conformant.
Read next: messagebus Service Related: Bus Events Reference · Bus-Client Dual-Emit Bridge (migration checklist) · Spec Tooling · Upcoming Changes