Spec ID: OVOS-COMMON-QUERY-1 · Version: 2 · Status: Draft
This specification defines the common query pipeline plugin — a
pipeline plugin that answers factual questions by holding a timed
contest among skills. During its match phase it broadcasts the
question, collects full answers from the skills that claim they can
answer, ranks them, and — if any answer clears a confidence
threshold — returns a Match carrying the winning answer for its
own handler to speak. When no answer clears the threshold, match
returns None and the pipeline continues to the next stage,
including fallback.
It builds on OVOS-MSG-1 (envelope, reply derivation, session
carrier), OVOS-PIPELINE-1 (pipeline-plugin contract, Match shape,
dispatch topic shape, handler-lifecycle trio, Match.updated_session,
reserved intent_name registry, §4.4 blocking-match allowance), and
OVOS-SESSION-1 (session field registry, omission rule).
The key words MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, and RECOMMENDED are used as in RFC 2119.
This specification defines:
- the common query plugin role (§2) — a pipeline plugin whose
matchblocks while it runs a multi-skill contest; - the reserved intent_name
common_query(§3); - the question gate (§4) — an optional pre-filter that rejects non-question utterances before any broadcast;
- the early-start optimisation (§5) — how the plugin overlaps its contest with upstream pipeline stages;
- the wants-to-answer poll (§6) — the fast ping/pong broadcast that filters skills down to plausible answerers;
- the answer collection (§7) — full-answer gathering;
- the filtering and selection (§8) — confidence filtering, denylist, opt-in fast-win, and ranking, applied against the live session;
- the match construction (§9) — the
Match, orNone; - the plugin handler (§10) — the trivial handler that speaks the selected answer;
- the skill-side protocol (§11);
- pipeline positioning (§12);
- the bus surface (§13);
- conformance (§14);
- tunable defaults (Appendix A) and confidence-range guidance for skill authors (Appendix B).
This specification does not define:
- vocabulary file format or question-classification algorithm — the gate MAY use any method; only the observable behaviour (accept / reject) is normative.
- skill-side answer generation — what a skill does internally to produce an answer is the skill's business. The spec fixes only the bus contract by which the skill reports its answer.
- the framework decorator or base class a skill author uses to participate — these are conveniences, not normative. Any component that honours the bus contract in §11 is a valid common-query skill.
- streaming answer delivery — the plugin collects complete answer strings before selecting; incremental assembly is out of scope.
The common query plugin is a pipeline plugin (PIPELINE-1 §3) that bundles its own handler. Its two roles are structurally separate:
- Matcher role (§6–§9): during
match, the plugin runs the full contest — ping/pong broadcast, parallel answer collection, filtering, and ranking. If an answer wins,matchreturns aMatchpointing at the plugin itself, carrying the answer inslots. If no answer wins,matchreturnsNoneand the orchestrator proceeds to the next pipeline stage, including fallback. - Handler role (§10): on receiving
<pipeline_id>:common_query, the plugin reads the selected answer from the dispatch payload and speaks it.
Because match returns None when no good answer is found, common
query never blocks fallback. This is its defining difference from
a plugin that claims an utterance speculatively and only later
discovers it cannot satisfy it.
OVOS-PIPELINE-1 §4.4 permits match to block on bus I/O but
SHOULDs plugins to return quickly and defer expensive work to the
handler, since match-phase latency is response latency. Common query
is a deliberate, documented exception: the answer is the claim
decision. The plugin cannot return a Match and defer collection to
the handler, because whether it claims at all depends on whether any
skill produces an answer above threshold (§9). The expensive work and
the routing decision are the same act, so it must happen in match.
Two consequences a deployer MUST accept:
- Bound interaction. OVOS-PIPELINE-1 §4.4 lets the orchestrator
bound each
matchby a timeout and skip a plugin that exceeds it. A deployment MUST set common query's match-timeout bound at or above its collection-window ceiling (§7.2), or the stage will be skipped mid-contest. The early-start optimisation (§5) is the intended way to keep the observedmatchduration low without shrinking the contest. - Positioning. Latency is bounded by the slowest claiming skill, up to the ceiling; §12 positions the stage to contain that cost.
Every other field of the pipeline-plugin contract applies unchanged:
the plugin is loaded and iterated per session.pipeline ordering,
subject to first-match-wins iteration (PIPELINE-1 §6.2) and denylist
filtering (PIPELINE-1 §5.2–§5.4).
The plugin instance has one pipeline_id (OVOS-PIPELINE-1 §3),
typically common_query. A deployment MAY reference it from several
session.pipeline entries under different match configurations;
those entries are matcher references, not additional actors.
The intent_name common_query is reserved in the
OVOS-PIPELINE-1 §7.3 registry.
| Reserved intent_name | Dispatch topic | Meaning |
|---|---|---|
common_query |
<pipeline_id>:common_query |
The plugin's own handler: speak the answer selected during match (§10). |
This intent_name is not registered via OVOS-INTENT-4. A
registration naming common_query via ovos.intent.register.* is
malformed per PIPELINE-1 §7.3.
Full-answer requests during match use the static topic
ovos.common_query.request, target named in the payload (§7). They are sent by the plugin,
not by the orchestrator, and are not dispatches — the colon form is
reserved for the PIPELINE-1 §7 dispatch shape (OVOS-MSG-1 §2.1.1).
The question gate is a cost-optimisation pre-filter, not the
primary quality mechanism. The confidence filter (§8) is the primary
quality gate: even with no gate, a non-question utterance produces no
answer above threshold, match returns None, and the pipeline
continues. The gate exists only to skip the broadcast cost — the
ping/pong round-trip and parallel skill invocations — for utterances
that obviously cannot produce a useful answer.
A plugin SHOULD apply a gate — a sentence-type classifier or any other cheap short-circuit — to avoid running the contest for utterances that are not question-like. Weather requests, music commands, timers, and plain statements have no business reaching a knowledge skill.
A deployment that omits the gate is still conformant — the confidence filter guarantees correctness either way — but pays the broadcast cost on every utterance. A gate trades that cost against the risk of false negatives (a genuine question wrongly rejected), so §4.2 biases the gate toward acceptance when in doubt.
When configured, the gate is a binary pre-filter:
- Accept — the utterance is plausibly a factual question. Proceed to the poll (§6).
- Reject — the utterance is clearly an action command or
otherwise not a factual question.
matchreturnsNonewithout broadcasting.
The gate MUST NOT be used as a confidence scorer or ranking layer; scoring belongs to the responding skills (§7–§8). The gate MAY use any combination of classifiers, vocabulary heuristics, or length thresholds. A deployment MAY skip the plugin's own gate and rely on an upstream classifier.
The gate SHOULD accept utterances that express a factual question and SHOULD NOT accept unambiguous action commands with no information intent.
The question/command boundary is fuzzy, so neither side of the gate can be stated as a testable MUST over the whole utterance space. The examples below are informative — they illustrate the intended bias, they do not enumerate a conformance set:
| Bias | Informative examples |
|---|---|
| accept | "what is the capital of France", "who invented electricity", "tell me about France" |
| reject | "play music", "set a timer", "turn off the lights" |
Over-acceptance wastes a round-trip; under-acceptance silently fails the user. When in doubt, accept.
Common query is a slow stage, but most of its latency can be hidden.
The plugin MAY subscribe to the utterance-entry topic
ovos.utterance.handle (OVOS-PIPELINE-1 §9.1) — the message the
orchestrator consumes to begin a new utterance, before pipeline
iteration starts. When this subscription is active, the plugin begins
the contest (gate → poll → answer collection) immediately, in
parallel with the upstream pipeline stages (stop, converse, intent
matchers). By the time the orchestrator calls match for the common
query stage, the raw responses MAY already be collected.
Language carve-out. OVOS-PIPELINE-1 §9.1 forbids a plugin to
re-derive the content language independently: the orchestrator
resolves it once and passes the resolved tag to every match call.
An early start runs before that resolution exists, so a plugin that
early-starts MUST treat the tag it starts under as provisional
— derived for speculative work only, never published. Specifically it
MUST NOT emit the provisional tag in any Match.lang, and it
MUST discard the whole cached contest when the provisional tag is
not equal to the lang argument the orchestrator later passes to
match (§5.1). The results of an early start are void unless the two
tags are equal. This is the only sanctioned pre-resolution language
derivation in this specification, and it is sanctioned because its
output can never reach the wire un-revalidated.
The early-start cache holds only the raw skill responses (§7) and
the utterance they were collected for. It does not hold a
selected answer. All filtering and selection (§8) is performed at
match time against the live session the orchestrator passes in —
never against the session snapshot the early start began with.
If the lang argument passed to match differs from the provisional
tag the early start collected under (§5), the cached responses
MUST be discarded and the contest re-run.
The cache MUST be keyed by the pair (session_id, utterance),
with session_id read from context.session. A cache entry is
consumed and evicted when match reads it. An entry is evicted
unconditionally when a new utterance arrives in the same session.
Utterance is the exact-match key. A cache entry MUST NOT be returned for any utterance other than the exact string it was collected for.
Which candidate is broadcast (n-best). match receives a list of
candidate utterances, while the ping, the request, and the cache key
carry a single string. The plugin MUST run the contest for the
first candidate in the list and MUST use that same string as
Match.utterance. This is the same rule PIPELINE-1 §4.1 states for a
plugin that does not track which candidate won, so a common query
Match is always consistent with the payload the orchestrator
forwards. A plugin MAY internally consider other candidates, but
the broadcast string, the cache key, and Match.utterance MUST
remain the first candidate.
When the gate accepts (or no gate is configured), the plugin runs a candidate poll (PIPELINE-1 §4.5) to filter the skill set down to those that plausibly can answer, on the topic pair
ovos.common_query.ping → ovos.common_query.pong
with the ping, pong, correlation, validity and window rules of that section. The poll exists to avoid invoking the expensive full-answer path (§7) — which may hit the network or a database — on skills that have no relevant knowledge. It is a cheap local filter gating an expensive operation, and that is its entire justification.
The candidate set is open: the broadcast carries no
destination, names no skill, and any subscribed skill MAY
respond. The session rides in context.session per OVOS-MSG-1 §4.
The ping payload:
{
"utterance": "what is the capital of France"
}| Field | Type | Required | Meaning |
|---|---|---|---|
utterance |
string | yes | The utterance being broadcast (§5.2, first candidate). |
The language the plugin runs the contest in is the lang
argument the orchestrator passed to match (PIPELINE-1 §9.1),
or — during an early start — the provisional tag of §5, which is
revalidated against that argument before anything is published. The
plugin MUST NOT re-derive the language from context.session
when a lang argument is available.
The pong payload is the PIPELINE-1 §4.5 shape — skill_id and
can_handle — plus two fields of this protocol:
{
"utterance": "what is the capital of France",
"skill_id": "wiki.test",
"can_handle": true,
"latency_ms": 800
}| Field | Type | Required | Meaning |
|---|---|---|---|
utterance |
string | yes | Echo of the ping's utterance. |
latency_ms |
number | no | Expected time in milliseconds to produce a full answer. A hint for sizing the collection window (§7.2), never a commitment or an extension of any bound. |
What a claim means. can_handle: true claims a place in the
answer round (§7), not an answer: the contest is decided there, on
the answers themselves. The claim MUST rest on local,
synchronous operations only — keyword matching, vocabulary lookup,
cached knowledge — and a skill MUST NOT perform network
requests, database queries, or other blocking I/O during the pong
phase. The full answer comes later (§7), where I/O is expected.
Because a decline changes nothing here — the window closes on
timeout or sufficiency regardless — a skill that cannot answer
SHOULD stay silent, and can_handle: false is permitted but
pointless. A skill that does not respond in time is treated as not
claiming.
The plugin MUST enforce a maximum poll window (Appendix A) and MUST stop waiting when it elapses; the responses it has by then are the claimants. Skills SHOULD respond within the deployer-configured pong bound (Appendix A). The plugin SHOULD close the window early once enough claimants are identified, and a deployment MAY proceed as soon as one claims.
The contest identifier is the PIPELINE-1 §4.5 correlation —
context.utterance_id (PIPELINE-1 §9.1.1), stamped once at the
lifecycle source and carried onto every derived Message. This
specification adds no correlation field of its own, and the
rule extends past the poll: the plugin MUST discard any pong,
full-answer response or other contest Message whose
context.utterance_id or session_id does not equal the active
contest's.
session_id alone would not separate two contests inside one
session, which is exactly what a repeated question or a shared
default session produces. Two lifecycles carry two utterance_ids,
and that is the whole discrimination.
For an out-of-band query (§12) the requester is the lifecycle
source and stamps utterance_id. A plugin receiving one without it
sits at lifecycle entry and MUST stamp a fresh one
(PIPELINE-1 §9.1.1) before deriving the contest's Messages.
After the poll window closes, the plugin requests full answers from all claiming skills in parallel.
The plugin sends ovos.common_query.request (broadcast, static
topic) once per claiming skill, naming the target in the payload:
{
"utterance": "what is the capital of France",
"skill_id": "wiki.test"
}| Field | Type | Required | Meaning |
|---|---|---|---|
utterance |
string | yes | The utterance to answer. |
skill_id |
string | yes | The skill being asked. Every common-query skill subscribes to the one topic and answers only when this names it — one payload equality check. Addressing is payload; the routing pair is owned by the reply swap (OVOS-MSG-1 §5.2) and never carries it. |
The language is the lang argument passed to match (§6.1), not a
value re-derived from the session. These are direct
plugin-to-skill messages: the orchestrator does not participate, does
not emit the handler-lifecycle trio for them, and skills MUST NOT
emit lifecycle signals in response.
Each skill emits its result on ovos.common_query.response
(static topic, derived via reply per OVOS-MSG-1 §5):
{
"utterance": "what is the capital of France",
"skill_id": "wiki.test",
"answer": "Paris is the capital of France.",
"conf": 0.85
}| Field | Type | Required | Meaning |
|---|---|---|---|
utterance |
string | yes | Echo of the request's utterance. |
skill_id |
string | yes | The responding skill's identifier. MUST equal the request's target (§7.1.1). |
answer |
string | conditional | The natural-language answer. MUST be present when the skill has one. |
conf |
number | conditional | Self-reported confidence in [0, 1]. MUST be present when answer is present (Appendix B). |
A skill that cannot produce an answer after all MUST still
respond, with no answer field, so early termination can fire.
Responses whose session does not match the active collection, or
whose context.utterance_id does not match it (§6.4), MUST be
discarded.
Identity is payload, verified against the contest's own state:
- Binding. The plugin MUST discard a response whose payload
skill_iddoes not name a skill it requested an answer from in this contest (§7.1). Without this check every downstream decision that consumesskill_id— the denylist (§8 step 2), deduplication, tie-breaking (§8.1), the answering-skill slot (§9) — acts on an identity the responder chose freely. The topics of this family are static strings; no identity is ever recovered by parsing a topic.
The collection window has two values (Appendix A): an initial window and a hard ceiling. The plugin MUST enforce the ceiling. The window closes at the earliest of: every claimant has responded (early termination, which the plugin MUST support), the current window expiring with no outstanding claimant, or the ceiling.
Extension trigger. The window starts at the initial value and extends toward the ceiling only while at least one claimant is still outstanding — a skill that sent a claiming pong and has sent no response yet. When the initial window expires with no outstanding claimant, the plugin MUST select immediately at that point; it MUST NOT wait out the remaining time to the ceiling. When claimants are still outstanding, the plugin MAY keep waiting, up to and never beyond the ceiling. A claimant that has not responded by the ceiling is treated as declining, and the ceiling is the absolute bound on the stage's contribution to response latency.
Sizing from latency_ms. When latency_ms values are available
from pongs (§6.2), the plugin SHOULD size the initial window to
the maximum latency_ms across claimants, clamped to the ceiling;
otherwise it SHOULD use the fixed initial window. latency_ms is a hint: it never raises the ceiling, an
implausible value (negative, non-numeric, above the ceiling) falls
back to the fixed initial window, and a plugin MAY ignore it
entirely. A skill cannot inflate the stage's budget; the worst it can
do is fail to be waited for.
Filtering and selection run at match time against the live
session (§5.1), in order:
- Minimum self-confidence. Discard responses whose
confis below the deployer-defined threshold (Appendix A). - Denylist. Discard responses whose
skill_idappears in the livesession.blacklisted_skills(PIPELINE-1 §5.3), as PIPELINE-1 §4.5 requires of a pong naming a denylisted skill in any candidate poll. - Fast-win (deployment-opt-in, default off). A deployment MAY
enable a fast-win rule: when enabled, if any surviving response
carries
conf ≥the fast-win threshold (Appendix A), the plugin MAY stop waiting immediately and select it, and the check MAY fire during collection (§7.2), short-circuiting the window. The rule is off by default:confis self-reported and skills share no calibrated confidence scale (Appendix B), so selecting the first response to cross a threshold turns answer selection into a nondeterministic latency race — the fastest confident skill wins, not the best one. Absent explicit deployer opt-in, the plugin SHOULD wait for all claimants whose reportedlatency_msis within the ceiling before selecting. - Selection. Select the highest-
confsurvivor. Ties MAY be broken by any deployer-defined heuristic; the algorithm is not normative. When a reranker is configured, the plugin SHOULD pass all survivors to it and use its ranking in place of rawconfordering; the reranker interface is a deployment concern.
If no response survives, the contest has no winner — match returns
None (§9).
After selection (§8):
- If no response survived, the plugin MUST return
None. The orchestrator proceeds to the next pipeline stage, including fallback. A contest with no winner is an expected outcome, not an error. - If an answer won, the plugin MUST return a
Matchwith:skill_id: the plugin's ownpipeline_idintent_name:"common_query"(reserved, §3)lang: thelangargument the orchestrator passed tomatch— the contest was run in that language (§6.1), so the plugin reports it back verbatim. The plugin MUST NOT report a value it derived itself, and in particular MUST NOT report an early-start provisional tag (§5).utterance: the first candidate from the input list, which is the string the contest was run for (§5.2)slots:{ "answer": "<the selected answer string>" }— the only field the handler needs (§10)updated_session: omitted
updated_session is omitted, not set to a copy of the inbound
session: PIPELINE-1 §4.1 defines an absent updated_session as
"carry the inbound session unchanged", which is precisely this
plugin's intent, and an echoed snapshot would claim a mutation the
plugin did not make.
The plugin MUST NOT mutate the session: common query does not
activate handlers, change persona_id, or modify any session field —
so it never emits an updated_session at all.
When the orchestrator dispatches <pipeline_id>:common_query, the
handler runs and fires the handler-lifecycle trio per PIPELINE-1 §8
(ovos.intent.handler.start, .complete, .error).
The handler is intentionally trivial — all contest work completed
during match (§6–§8). It:
- Reads
answerfromslotsin the dispatch payload. - Speaks it via
ovos.utterance.speakper OVOS-PIPELINE-1. - Emits
ovos.intent.handler.complete.
The handler MUST NOT re-dispatch to skills or perform additional
collection. ovos.intent.handler.error is reserved for crashes and
unrecoverable handler failures.
A skill participates by handling two topics (see §13 for the full bus surface):
- On
ovos.common_query.ping, perform a fast local check for a likely answer. If yes, respond onovos.common_query.pongwithcan_handle: true, the echoedutterance, its ownskill_id, and optionallylatency_ms— deriving the pong viareplysocontext.utterance_idrides along (§6.4). If no, stay silent. - On
ovos.common_query.requestnaming it indata.skill_id, produce the best answer — network calls, DB queries, and full generation are appropriate here — and emit it onovos.common_query.response(viareply, OVOS-MSG-1 §5) with the echoedutterance, its ownskill_id,answer, andconf(§7.1.1). A request naming a different skill is not addressed to it and MUST be ignored. If no answer can be produced, emit the response with noanswerfield so early termination can fire. - The skill MUST NOT call
ovos.utterance.speakfrom itscommon_queryhandler. Speaking is the plugin's responsibility (§10).
Common query is a slow stage. A deployment SHOULD place it after all intent-matching stages and before the fallback stage(s): intent matchers are tried first, and fallback still runs if common query finds no answer. When a persona catch-all (OVOS-PERSONA-1 §10) is also present, common query precedes it, so deterministic question-answering is preferred over a persona's generated reply.
session.pipeline: [
"stop_high",
"converse",
"skill_high",
"skill_medium",
"common_query",
"fallback_medium",
"fallback_low"
]
With early start enabled (§5), the contest begins as the utterance arrives, so its wall-clock cost is largely amortised against the upstream stages by the time the orchestrator reaches it. Without early start, the stage blocks for the full collection window.
| Topic | Direction | Purpose | Defined in |
|---|---|---|---|
ovos.common_query.ping |
plugin → all skills | Wants-to-answer poll | §6.1 |
ovos.common_query.pong |
skill → plugin | Claim, via reply |
§6.2 |
ovos.common_query.request |
plugin → claiming skill (target in data.skill_id) |
Full-answer request (during match) | §7.1 |
ovos.common_query.response |
claiming skill → plugin | Full answer or decline, via reply |
§7.1, §11 |
<pipeline_id>:common_query |
orchestrator → plugin | Handler dispatch (reserved intent_name) | §3, §10 |
The one colon-form topic (<pipeline_id>:common_query) is the
orchestrator's dispatch and follows the PIPELINE-1 §7 dispatch shape.
Dotted-form topics (ovos.common_query.request,
ovos.common_query.response) are plugin- and skill-emitted
non-dispatch messages per MSG-1 §2.1.1; the target and responder
identities travel in data.skill_id (§7.1.1) — never in the topic.
ovos.common_query.ping is a broadcast. Pong and
answer responses are both derived via reply (OVOS-MSG-1 §5). Every
poll/response message is correlated by context.utterance_id
(§6.4) and echoes the utterance.
- expose a blocking
match(utterances, lang, session) → Match | Noneper PIPELINE-1 §4 (§2.1); - broadcast
ovos.common_query.pingand collectovos.common_query.pongwithin a bounded poll window (§6.3); - correlate every contest by
context.utterance_id(PIPELINE-1 §9.1.1), stamping a fresh one only at lifecycle entry for an out-of-band query that arrived without it, and discard pongs and responses whoseutterance_idor session does not match the active contest (§6.1, §6.4, §7.1); - discard a response whose payload
skill_iddoes not name a skill this contest requested an answer from (§7.1.1); - request full answers via
ovos.common_query.request, one per claimant named indata.skill_id, in parallel, and collect within a bounded window (§7.1, §7.2); - extend the collection window past the initial value only while a
claimant is outstanding, never past the ceiling, and never on the
strength of a reported
latency_ms(§7.2); - apply confidence filtering and the denylist against the live
session passed to
match, not against any early-start snapshot (§5.1, §8); - honour the live
session.blacklisted_skillsitself (§8 step 2) — the PIPELINE-1 §5.3 orchestrator backstop cannot see the answering skill, becauseMatch.skill_idis the plugin'spipeline_id; - run the contest for the first candidate utterance and report that
same string as
Match.utterance(§5.2, §9); - return
Nonewhen no response survives, letting the pipeline reach fallback (§9); - return a
Matchwithskill_id= its ownpipeline_id,intent_name="common_query",lang= thelangargument passed tomatch, andslots.answer= the selected answer when one wins (§9); - not mutate the session —
Match.updated_sessionis omitted (§9); - treat any language tag derived before the orchestrator's resolution
as provisional, never publish it, and discard the early-start
contest unless it equals the
langargument (§5, §5.1); - key all contest state by
session_idfromcontext.session, alongsideutterance_id(§6.4); - speak the selected answer from
slots.answerin the handler without re-dispatching to skills (§10).
- apply a question gate — classifier or other cheap short-circuit — to skip the contest for non-question-like utterances; gate-less deployments are conformant but pay the broadcast cost on every utterance (§4);
- subscribe to the utterance-arrival event and run the contest early, in parallel with upstream stages (§5);
- size the collection window from claimants'
latency_ms, treating it as a hint and ignoring implausible values (§7.2); - close the collection window on all-responded, or on fast-win only when the deployer has enabled it (§7.2, §8 step 3);
- use a reranker when configured (§8 step 4).
- on
ovos.common_query.ping, perform only a fast local check; MUST NOT perform network requests or blocking I/O during the pong phase (§6.2); - echo the
utteranceverbatim in every pong and response (the evaluated candidate, §5.2), and derive both viareplysocontext.utterance_idpropagates for correlation (§6.2, §6.4, §7.1); - emit answers on
ovos.common_query.responseviareply(§7.1, §11); - report its own
skill_idin the payload, and ignore any request whosedata.skill_idnames another skill (§7.1.1); - include
confwheneveransweris present (§7.1); - respond even when no answer can be produced (no
answerfield), so early termination can fire (§7.2); - not call
ovos.utterance.speakfrom thecommon_queryhandler (§11); - not emit handler-lifecycle signals in response to
ovos.common_query.request(§7.1).
- respond to the pong within the deployer-configured bound (Appendix A, §6.2);
- report
confusing the Appendix B ranges so values interoperate; - include
latency_msin its pong so the plugin can size an adaptive collection window (§6.2); - ignore unknown fields in
ovos.common_query.ping.
All values are deployer-configurable; these are the RECOMMENDED defaults. They are guidance, not protocol — a deployment that tunes them is conformant.
| Knob | Default | Section |
|---|---|---|
| Pong response bound (skill-side target) | 100 ms | §6.2 |
| Poll-window ceiling | 500 ms | §6.3 |
| Collection-window initial | 3 s | §7.2 |
| Collection-window ceiling | 5 s | §7.2 |
| Minimum self-confidence | 0.5 | §8 step 1 |
| Fast-win threshold (only when fast-win is enabled; default off) | 0.9 | §8 step 3 |
conf is self-reported and not calibrated across skills. These
ranges are RECOMMENDED so independently authored skills produce
comparable values; a reranker (§8 step 4) is the proper fix when
calibration matters.
| Range | Meaning |
|---|---|
| 0.0–0.3 | weak signal; something, but low certainty |
| 0.3–0.5 | partial match; can attempt an answer |
| 0.5–0.7 | reasonable answer; fairly confident |
| 0.7–0.9 | strong answer; confident |
| 0.9–1.0 | definitive answer; certain (use sparingly) |
- Utterance Lifecycle and Pipeline Specification (OVOS-PIPELINE-1)
— the pipeline-plugin contract, the §4.4 blocking-match allowance
and latency discipline, the
Matchshape, the dispatch model, the handler-lifecycle trio, theovos.utterance.handleentry topic (§9.1), and the reserved intent_name registry. - Bus Message Specification (OVOS-MSG-1) — the envelope,
context.sessioncarrier, andreplyderivation used for pong and answer responses. - Session Specification (OVOS-SESSION-1) — the
field-registry mechanism, the omission rule, and
session.lang. - Session Lifecycle and State Ownership Specification (OVOS-SESSION-2) — session-keyed state and mutation boundaries.