Skip to content

Design record: why union slot sets + required_slots over same_as / per-template slots #57

Description

@JarbasAl

This issue records the design discussion that led to PR #56. It explains the problem, the alternatives considered, and why the chosen approach was adopted.

Problem

The current specs (INTENT-1 §5.5, INTENT-3 §5.1) require every template in one .intent file to declare the identical set of slot names. This forces authors to split phrasings into separate files when slot sets differ — even when those phrasings represent the same command. Example:

(play|put on) {query}
(play|put on) {artist} by {album}

Today these must be two separate .intent files with two separate handlers, or two separate registrations bound to the same handler code, with the system treating them as unrelated intents for blacklist / analytics purposes.

Additionally, there is no way for a handler to know which slots it can safely rely on. INTENT-3 §7.1 currently says handlers must cope with any slot being missing, which makes defensive code verbose and error-prone.

Alternatives considered

Alternative A: same_as / alias_of mechanism

Add an optional same_as field to intent registrations. An alias intent is a full, normal registration that resolves to another intent's handler at dispatch time. Pipeline plugins MAY merge aliases with their canonical or keep them separate.

Why rejected:

  • Adds alias bureaucracy (canonical + alias registrations, dispatch rewrite, manifest semantics, circular-alias rejection).
  • Does not solve the template-authoring problem directly — it just provides a linkage mechanism around it.
  • Slot compatibility between alias and canonical is messy: either the canonical must declare a superset of all alias slots (awkward), or aliases can inject arbitrary slots (breaks stable schema).
  • The keyword / template mix case is already handled by INTENT-4 §3.2 — both methods can register the same (skill_id, intent_name) independently.

Alternative B: Relax slot consistency + per-template required_slots

Keep §5.5 strict, but allow per-template required_slots annotations inside .intent files. Example: ("play {artist} by {album}", required_slots=["artist", "album"]).

Why rejected:

  • Requires changing the flat samples array model to a structured object model, which is a larger wire-format break.
  • The simpler "one required_slots list per intent" covers the 90% case without per-template granularity.
  • If a framework needs per-template granularity, it can express it by splitting into separate intents — the whole point of relaxing §5.5 is to make that unnecessary.

Alternative C: Add .required_slots as a locale resource file

Create a sixth file role under locale/, e.g. play_music.required_slots, listing slot names.

Why rejected:

  • required_slots contains technical slot identifiers (query, artist), not localized natural-language data. Translating them would break handler code.
  • The locale/ tree is for i18n resources; metadata belongs with the other non-locale registration fields (handler binding, enable/disable state).
  • Frameworks can still offer file-based authoring as a loader convention without the spec mandating a new resource role.

Alternative D: Orchestrator MAY backstop required_slots

In an earlier draft, the orchestrator backstop was MAY because the manifest is a passive, potentially incomplete index built from observed broadcasts.

Why rejected:

  • The orchestrator and pipeline plugins are co-located in the same process (PIPELINE-1 §2) and consume the same bus broadcasts. If the orchestrator missed a registration, the engine missed it too.
  • The orchestrator already actively gatekeeps with blacklisted_skills / blacklisted_intents (MUST backstop). required_slots backstop is the same shape.
  • Making it MUST gives handlers the strongest guarantee regardless of engine bugs.

Chosen approach (PR #56)

  1. Relax §5.5 for .intent to union semantics — templates in one .intent MAY declare different slot sets. The engine extracts only the slots from the template that matched.
  2. Add required_slots to the bus registration — an optional array of slot names on ovos.intent.register.template. The engine MUST decline a match if any required slot is absent.
  3. Orchestrator MUST backstop — after the denylist check, the orchestrator verifies the match's slot map contains every required slot; if absent, it treats the match as declined (PIPELINE-1 §6.2).
  4. Keep .dialog strict — caller-supplied fill still requires consistent slot sets.
  5. No keyword-intent required_slots — keyword intents already have required vocabularies (INTENT-3 §4.2) serving an analogous role.
  6. Appendix patterns — practical authoring examples for union slot sets and required_slots (appendix/patterns.md §3.4).

This is minimal blast radius: it changes authoring rules (INTENT-1/3), the bus payload (INTENT-4), the orchestrator backstop (PIPELINE-1), and adds appendix examples — but does not invent new file types, new dispatch mechanics, or alias resolution logic.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions