Spec ID: OVOS-FALLBACK-1 · Version: 2 · Status: Draft
This specification defines the fallback pipeline plugin — a pipeline plugin that handles utterances no earlier stage claimed. It maintains a registry of fallback skills, constructs an ordered handler pool from that registry and the session's preferences, queries skills in pool order to find a willing handler, and dispatches to the first one that claims the utterance.
It builds on four companion specifications:
- the Utterance Lifecycle and Pipeline Specification
(OVOS-PIPELINE-1) — the pipeline-plugin contract, the
Matchshape, dispatch, the handler-lifecycle trio, the pipeline composition model, and the reserved-intent-name registry; - the Bus Message Specification (OVOS-MSG-1) — the envelope, routing keys, session carrier, and derivations every Message defined here travels in;
- the Session Carrier Wire Shape Specification (OVOS-SESSION-1) — the session field registry and the omission rule;
- the Intent and Entity Registration Bus Contract (OVOS-INTENT-4) — the session-scoped registration model.
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, and RECOMMENDED are used as in RFC 2119.
This specification defines:
- the fallback plugin role (§2) — a pipeline plugin that delegates to registered fallback skills;
- skill registration (§3) — how a skill declares itself as a fallback handler with a default ordering priority;
- session fields (§4) — the two session-resident fields that control pool ordering and access control;
- pool construction (§5) — how the ordered handler pool is derived from registration, session preference, and policy;
- the match contract (§6) — the sequential per-skill query, selection algorithm, and Match shape;
- the dispatch and handler contract (§7) — what the selected skill receives;
- pipeline positioning (§8) — where fallback stages sit and how multiple stages interleave with other plugins;
- bus surface (§9);
- conformance (§10).
It does not define:
- what a fallback skill does internally — whether it queries a language model, returns a canned response, or calls an external service is the skill's business;
- per-stage range boundaries — which priority numbers belong to which stage is deployment configuration; §3.3 provides non-normative guidance on the recommended tiers;
- query timeout values — these are deployment-defined.
A fallback pipeline plugin is a pipeline plugin (PIPELINE-1 §3) that:
- occupies one or more positions in
session.pipeline(§8); - maintains a registry of skills that have declared themselves fallback handlers, each with a default ordering priority (§3);
- at each match call, constructs an ordered handler pool from the registry, session preference, and policy (§5);
- queries pool members in order until a willing skill is found (§6);
- returns a
Matchdelegating to that skill, orNoneif the pool is exhausted (§6).
The fallback plugin does not handle utterances itself.
The defining property of a fallback skill is that it evaluates the utterance using its own internal logic rather than relying on an intent model registered with the pipeline. Regular skills declare their coverage through INTENT-4 registrations — vocabulary, templates, or entity patterns — and a pipeline plugin selects them by matching those patterns against the utterance. A fallback skill declares no intent patterns; instead it receives the raw utterance at query time (§6.1) and decides for itself whether it can respond. This is the appropriate pattern when an utterance domain cannot be reliably modelled as a grammar or template: open-domain question answering, natural-language database queries, language-model completions, and any skill whose coverage is determined programmatically at runtime.
This mechanism is analogous to OVOS-CONVERSE-1's per-skill converse poll, where active handlers evaluate the current utterance themselves before the plugin dispatches to one of them.
A skill that wishes to receive fallback dispatches emits:
ovos.fallback.register
| Field | Type | Required | Meaning |
|---|---|---|---|
skill_id |
string | yes | The skill's identity. MUST equal context.skill_id of this Message. |
priority |
integer | yes | Default ordering hint. Lower values sort earlier when no session preference overrides. |
The plugin adds the skill to its registry. Re-registration with
the same skill_id replaces the prior entry. The plugin MUST NOT
index a registration where the payload skill_id differs from
context.skill_id.
ovos.fallback.deregister
| Field | Type | Required | Meaning |
|---|---|---|---|
skill_id |
string | yes | The skill's identity. |
Removes the skill from the registry. Unknown skill_id is a no-op.
Priority values have no normative meaning beyond ordering — lower values appear earlier in the pool. This section provides non-normative guidance for skill authors and deployers.
The recommended convention divides the space into three tiers:
| Range | Tier | Typical skills |
|---|---|---|
| 0–49 | High confidence | Skills that search structured, bounded knowledge sources — local databases, domain-specific knowledge bases, FAQ indexes, entity lookups. These know quickly whether they have a relevant answer and are expected to be accurate when they claim the utterance. |
| 50–74 | Medium confidence | Skills that perform broader retrieval — web search, general knowledge queries — where answer quality depends on the query. They should handle the utterance if the topic is in scope but may decline when it is not. |
| 75–100 | Low confidence | General-purpose language-model chatbots and catch-all handlers that will attempt any utterance without domain restriction. These run last; they provide a response when nothing else can, not as a first choice. |
This three-tier mapping corresponds directly to the
fallback_high / fallback_medium / fallback_low multi-stage
pipeline example in §8.2.
A skill author uncertain which tier applies SHOULD register at a higher number rather than a lower one. Pre-empting a more precise handler with a low-confidence catch-all degrades response quality silently.
Default response skill. A voice assistant SHOULD always
produce an answer rather than silent failure. Every deployment
SHOULD include a catch-all fallback skill — registered at the
highest priority number in the pool (e.g. priority: 100) — that
unconditionally returns can_handle: true and responds with a
graceful "I don't know how to answer that" message. This skill is
the last entry in session.fallback_handlers if that field is
set, and the last resort after all higher-confidence handlers have
declined. Without it, an utterance that no skill can handle
produces ovos.intent.unmatched with no user-facing response.
Registration is session-scoped per OVOS-INTENT-4 §11.1: the
plugin keys each entry by context.session.session_id of the
registration Message. Skills registered under "default" are
available to all sessions. Skills registered under a specific
session_id extend the pool for that session only.
This specification claims one optional session field per OVOS-SESSION-1 §2.1.
| Field | Wire type | Owner |
|---|---|---|
fallback_handlers |
array of string | §4 (this spec) |
session.fallback_handlers is an ordered list of skill_ids
expressing a session-level preference for the fallback handler
order. When present, this list is the primary ordering input to
pool construction (§5). When absent, registered priority
determines order.
The list MAY be partial. Skills not listed but registered and available are appended after the listed skills, sorted by registered priority ascending.
Per-skill access control uses the existing
session.blacklisted_skills field (OVOS-SESSION-1 §3) — no
separate fallback-specific denylist is needed. To block all
fallback handling for a session, add the fallback stage(s) to
session.blacklisted_pipelines, or omit them from
session.pipeline.
On each match call the plugin constructs the effective handler
pool:
- Preference. If
session.fallback_handlersis present and non-empty, use it as the leading order. Append any registered skills not in the list, sorted by registered priority ascending. If absent, sort all registered skills by registered priority ascending. - Stage range. If this plugin instance is configured with a priority range (§8.2), retain only skills whose registered priority falls within that range.
- Availability. Retain only skills present in the registry for
the current
session_id(including"default"registrations per §3.4). - Policy. Remove any
skill_idpresent insession.blacklisted_skills(OVOS-SESSION-1 §3).
The result is the ordered effective pool. An empty pool causes the
plugin to return None immediately. No later stage adds what an
earlier stage removed.
When the effective pool is non-empty the plugin queries skills one at a time in pool order. For each skill it sends:
<skill_id>.fallback.ping
using the dotted addressed form, derived via .reply() from
the inbound utterance Message (OVOS-MSG-1 §5.2), so that
context.session and routing metadata propagate automatically.
Payload:
| Field | Type | Required | Meaning |
|---|---|---|---|
utterances |
array of string | yes | The candidate utterance list the skill should evaluate. |
lang |
string | yes | The resolved BCP-47 language tag. |
The skill uses these to run its own evaluation logic and decide whether it can produce a meaningful response. This is the point at which the fallback skill parses the utterance — it may query a knowledge base, run a classifier, call an LLM, or apply any other internal logic. The reply carries only the decision:
The queried skill replies with:
<skill_id>.fallback.pong
| Field | Type | Required | Meaning |
|---|---|---|---|
skill_id |
string | yes | The responding skill's identity. MUST equal the topic prefix. |
can_handle |
bool | yes | Whether this skill is willing to handle the current utterance. |
The plugin waits for each skill's reply before advancing to the
next. A skill that does not respond within a deployment-defined
timeout is treated as can_handle: false and skipped.
Bus-exchange exception. The per-skill query cycle is a documented exception to PIPELINE-1 §4.4's low-latency guidance, justified because fallback stage(s) are positioned after all other intent-matching stages (§8). No further stages are blocked during the exchange, and the query terminates as soon as a willing skill is found. This pattern follows the precedent of OVOS-CONVERSE-1's per-skill converse poll.
The plugin selects the first skill in pool order whose
can_handle reply is true. If the pool is exhausted with no
willing skill the plugin returns None, and the pipeline emits
ovos.intent.unmatched (PIPELINE-1 §9.4).
| Field | Value |
|---|---|
skill_id |
The selected skill's skill_id. |
intent_name |
"fallback" — reserved per PIPELINE-1 §7.3. |
lang |
The resolved BCP-47 language tag. |
utterance |
The utterance string passed to match. |
slots |
Empty. |
updated_session |
Present if the plugin mutates session state. |
The skill_id targets the selected skill, not the fallback
plugin's own pipeline_id.
The orchestrator dispatches <skill_id>:fallback per PIPELINE-1
§7, firing the standard handler-lifecycle trio. The dispatch
payload is the standard shape: lang, utterance, slots.
The selected skill's handler:
- MAY emit
ovos.utterance.speak(PIPELINE-1 §9.6); - MAY act silently;
- MUST complete within the handler lifecycle (PIPELINE-1 §8).
No fallback-specific response protocol is required beyond
subscribing to <own_skill_id>:fallback.
Fallback stage(s) SHOULD be placed after all deterministic intent-matching stages and after the persona stage (if present). Utterances that reach a fallback stage were not claimed by any earlier stage.
Every deployment SHOULD include a catch-all fallback skill
registered at the bottom of the priority pool (see §3.3) that
always returns can_handle: true. This ensures the user receives
a response to every utterance rather than silent ovos.intent.unmatched.
A deployment MAY load multiple fallback plugin instances at
different positions in session.pipeline. Each instance is
configured with a priority range — a [min, max] integer
interval — that restricts which registered skills it considers
(§5 step 2). This allows fallback skills to be interleaved with
other pipeline stages.
Example:
session.pipeline: [
"stop_high",
"converse",
"intent_high",
"fallback_high", ← priority range [0, 49]
"intent_medium",
"fallback_medium", ← priority range [50, 74]
"persona",
"fallback_low" ← priority range [75, 100]
]
A skill registered at priority: 10 is queried by fallback_high
before intent_medium runs. A skill registered at priority: 80 is
queried by fallback_low only after both intent_medium and persona
have declined. A single-stage deployment sets no range restriction.
Within any stage, pool construction and session ordering (§5) work identically regardless of how many stages are present.
| Topic | Form | Direction | Purpose |
|---|---|---|---|
ovos.fallback.register |
broadcast | skill → fallback | Register as a fallback handler (§3.1). |
ovos.fallback.deregister |
broadcast | skill → fallback | Deregister (§3.2). |
<skill_id>.fallback.ping |
dotted addressed | fallback → skill | Query: willing to handle this utterance? (§6.1). |
<skill_id>.fallback.pong |
dotted addressed | skill → fallback | Reply: willing or not (§6.1). |
<skill_id>:fallback |
dispatch | orchestrator → skill | Dispatch to the selected skill (§7). |
- expose a
match(utterances, lang, session) → Match | Noneoperation per PIPELINE-1 §4; - maintain a session-scoped registry of skills with integer priorities per §3.4;
- subscribe to
ovos.fallback.registerandovos.fallback.deregister(§3); - reject any registration where payload
skill_id≠context.skill_id(§3.1); - construct the effective handler pool per §5 on each match call;
- query skills sequentially via
<skill_id>.fallback.pingand await<skill_id>.fallback.pongbefore advancing (§6.1); - select the first willing skill in pool order (§6.2);
- return a
Matchwithintent_name: "fallback"targeting the selected skill (§6.3); - return
Nonewhen no skill in the pool is willing (§6.2).
- apply a per-skill query timeout and treat non-response as
can_handle: false(§6.1); - when configured with a priority range, apply it as §5 step 2.
- mutate session state via
Match.updated_session(§6.3).
- emit
ovos.fallback.registerwith itsskill_idandprioritybefore receiving fallback dispatches (§3.1); - subscribe to
<own_skill_id>.fallback.pingand reply with<own_skill_id>.fallback.pong(§6.1); - subscribe to
<own_skill_id>:fallbackto receive dispatches (§7).
- position fallback stage(s) after deterministic intent-matching
and persona stages in
session.pipeline(§8.1).
- OVOS-PIPELINE-1 — pipeline-plugin contract, Match shape, dispatch, reserved intent-name registry (§7.3).
- OVOS-MSG-1 — envelope, derivations, and routing keys.
- OVOS-SESSION-1 — session field registry;
session.blacklisted_skills. - OVOS-INTENT-4 — session-scoped registration model (§11).
- OVOS-CONVERSE-1 — the dotted-addressed per-skill query pattern this specification follows.
- OVOS-PERSONA-1 — the persona stage that precedes fallback.