OVOS-CONVERSE-1: Active Handlers and Interactive Response#25
Conversation
|
Warning Review limit reached
More reviews will be available in 17 minutes and 12 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
📝 WalkthroughWalkthroughAdds OVOS-CONVERSE-1 (draft) specification: defines ChangesOVOS-CONVERSE-1 Specification and Session Semantics
Sequence Diagram (high-level match flow): sequenceDiagram
participant Orchestrator
participant ConversePlugin
participant OwnerSkill
Orchestrator->>ConversePlugin: invoke match
ConversePlugin->>ConversePlugin: check session.response_mode
alt response_mode unexpired
ConversePlugin->>OwnerSkill: dispatch <owner_id>:response (deliver next utterance)
else no response_mode
ConversePlugin->>OwnerSkill: <owner_id>.converse.request (poll)
OwnerSkill-->>ConversePlugin: <owner_id>.converse.response (result: true/false, error_code)
ConversePlugin->>Orchestrator: return reserved intent `converse` or proceed to next handler
end
🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues:
Possibly related PRs:
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…NVERSE-1) OVOS-CONVERSE-1 (PR #25) claims two session fields under SESSION-1 §2.1's field registry: - active_handlers — recency stack of [owner_id, activated_at] pairs - response_mode — single owner_id string when set, absent otherwise Add both to the §3 informative roster of claimed fields and to the §6 owner-spec list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update the forward-reference now that the companion imperative continuous-dialog specification (OVOS-CONVERSE-1, PR #25) is in flight. Names the two session fields it owns (response_mode, active_handlers) and cites the §7 evaluation- order rule that disambiguates the two surfaces for the orchestrator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@converse.md`:
- Around line 253-267: Tighten the authorization rule for direct mutations of
session.active_handlers: update the text around MSG-1 / CONTEXT-1 to state that
if the emitter's self-attribution (as defined by OVOS-INTENT-4 §3.1 for skills
via context["skill_id"] or PIPELINE-1 §3.1 for plugins via
context["pipeline_id"]) is missing or cannot be resolved, the orchestrator MUST
reject any direct mutation that adds, removes, or re-orders entries whose
owner_id differs from the emitter; keep the existing allowance that an emitter
MAY add/refresh/remove entries where owner_id equals its resolved
self-attribution and explicitly call out that unresolved identity => rejection.
- Around line 339-341: The cross-reference is wrong: the sentence about a
transformer mutating `session.response_mode` currently points to §5.5 but should
point to §5.4 where transformer-driven cancellation is defined; update the
reference in the sentence that mentions `session.response_mode` to cite §5.4
(and adjust any nearby parenthetical or explanatory text if it assumes
session-expiry behavior from §5.5).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| Any component MAY mutate `session.active_handlers` in place | ||
| through the direct-mutation pathway of MSG-1 (the same pathway | ||
| CONTEXT-1 §5.3 cites for intent-context entries), subject to the | ||
| authorization rule below. The canonical use case is an owner | ||
| removing itself from the list after declining a converse poll | ||
| without going through the dispatch decline path. | ||
|
|
||
| **Authorization.** A direct mutation that adds, removes, or | ||
| re-orders an entry whose `owner_id` differs from the emitter's | ||
| **self-attribution** (per OVOS-INTENT-4 §3.1 for skills via | ||
| `context["skill_id"]`, or per PIPELINE-1 §3.1 for pipeline plugins | ||
| via `context["pipeline_id"]`) **MUST** be rejected by the | ||
| orchestrator. An emitter **MAY** freely add, refresh, or remove | ||
| the entry whose `owner_id` equals its own self-attribution. | ||
|
|
There was a problem hiding this comment.
Tighten direct-mutation authorization for non-attributed emitters.
Any component MAY mutate is too broad versus the self-attribution model (skill_id / pipeline_id). Please explicitly require rejection when emitter identity is missing or unresolvable, otherwise mutation authority is ambiguous for non-handler emitters.
Suggested spec text adjustment
-Any component MAY mutate `session.active_handlers` in place
+Any component MAY request mutation of `session.active_handlers` in place
through the direct-mutation pathway of MSG-1 ...
@@
-**Authorization.** A direct mutation that adds, removes, or
+**Authorization.** The orchestrator MUST first resolve emitter self-attribution
+from Message.context (`skill_id` or `pipeline_id`). If neither is present,
+the mutation MUST be rejected. A direct mutation that adds, removes, or
re-orders an entry whose `owner_id` differs from the emitter's
**self-attribution** ... **MUST** be rejected by the orchestrator.Also applies to: 1041-1043
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@converse.md` around lines 253 - 267, Tighten the authorization rule for
direct mutations of session.active_handlers: update the text around MSG-1 /
CONTEXT-1 to state that if the emitter's self-attribution (as defined by
OVOS-INTENT-4 §3.1 for skills via context["skill_id"] or PIPELINE-1 §3.1 for
plugins via context["pipeline_id"]) is missing or cannot be resolved, the
orchestrator MUST reject any direct mutation that adds, removes, or re-orders
entries whose owner_id differs from the emitter; keep the existing allowance
that an emitter MAY add/refresh/remove entries where owner_id equals its
resolved self-attribution and explicitly call out that unresolved identity =>
rejection.
| A transformer that mutates `session.response_mode` while a wait | ||
| window is open **MUST** be understood by the deployer to | ||
| cancel that window per §5.5. |
There was a problem hiding this comment.
Fix incorrect cross-reference for transformer-triggered cancellation.
This points to §5.5, but transformer-driven cancellation is defined in §5.4. §5.5 is session-expiry handling.
Suggested fix
-cancel that window per §5.5.
+cancel that window per §5.4.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| A transformer that mutates `session.response_mode` while a wait | |
| window is open **MUST** be understood by the deployer to | |
| cancel that window per §5.5. | |
| A transformer that mutates `session.response_mode` while a wait | |
| window is open **MUST** be understood by the deployer to | |
| cancel that window per §5.4. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@converse.md` around lines 339 - 341, The cross-reference is wrong: the
sentence about a transformer mutating `session.response_mode` currently points
to §5.5 but should point to §5.4 where transformer-driven cancellation is
defined; update the reference in the sentence that mentions
`session.response_mode` to cite §5.4 (and adjust any nearby parenthetical or
explanatory text if it assumes session-expiry behavior from §5.5).
Add a §7 subsection codifying the registry mechanism by which other specs reserve intent_names and modify dispatch semantics. Currently in force: converse and response (both per OVOS-CONVERSE-1, PR #25), both using dispatch suppression (the reserving spec's match-phase or delivery-path code emits the dispatch directly; the orchestrator MUST NOT re-dispatch on the returned Match). Renumbers prior §7.3 (in-process equivalence) to §7.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add new error_code 'reserved_name' and a §3.5 cross-reference to OVOS-PIPELINE-1 §7.3's reserved-intent-name registry. The orchestrator MUST reject registration payloads that name an intent_name reserved by a companion specification. Currently reserved: converse and response, both by OVOS-CONVERSE-1 (PR #25). The reservation registry is owned by PIPELINE-1; this spec is the enforcement point at registration time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CONVERSE-1 §3.4 (PR #25) explicitly cites the metadata-transformer hook as the recommended position for mutating session.active_handlers and session.response_mode. Add this to the §3.3 list of permitted mutations, with the §5.4 cancellation-semantics back-reference for mid-wait holder changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-turn gap OVOS-CONVERSE-1 (PR #25) fills the multi-turn conversation gap that §7 previously listed as planned work. Update §1.2 stack description to include it, and drop the §7 gap entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ymorphism; drop dispatch suppression Three coordinated changes to make the pipeline-plugin model fully uniform and to remove a structural exception that OVOS-CONVERSE-1 required: - §4.2 — drop the "plugin MUST NOT emit during match" prohibition. The match contract is the single obligation: return a Match or null. Plugins MAY emit bus Messages during match if their matching strategy requires it (a converse plugin polling active handlers is the canonical case). The spec retains a SHOULD NOT on session-field mutation during match — declined-match corruption is still real for the few session fields used by later iteration stages, but bus emissions are not policed. - §7.0 (new) — codify identifier polymorphism explicitly. Three component shapes own dispatched handlers: plain skill, plain pipeline plugin with bundled handler, hybrid plugin-skill. For the hybrid case, "if a pipeline plugin registers any intent under OVOS-INTENT-4, that plugin's pipeline_id MUST equal its skill_id" — one identifier, two roles. skill_id conceptually names the voice application; pipeline_id names the matching engine; a component that is both carries one identifier filling both roles. - §7.1 — replace the exclusive "stamp skill_id XOR pipeline_id" rule with two independent stamps. context["skill_id"] applies when owner_id is registered as a skill; context["pipeline_id"] applies when owner_id is loaded as a pipeline plugin. For a hybrid plugin-skill both stamps land and carry the same identifier. - §7.3 — simplify the reserved-intent-name registry. Drop the "dispatch suppression" clause that bent §6's match-then- dispatch flow. A reservation is now a namespace lease only: skills can't register the name, and a pipeline plugin's Match bearing the name dispatches normally per §7 (orchestrator emits <owner_id>:<reserved_name>, handler subscribed to that topic runs). Updated the converse/response entries in the table to reflect this — the converse plugin polls active handlers via whatever wire shape OVOS-CONVERSE-1 defines (no longer the dispatch topic), then returns a Match that the orchestrator dispatches normally. Also updates §4.1 Match.owner_id description to cite §7.0's three-shape model rather than the prior implicit OR. Companion PR: OVOS-CONVERSE-1 (#25) needs a matching restructure to drop its dispatch-suppression dependency and rely on the relaxed §4.2 instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a §7 subsection codifying the registry mechanism by which other specs reserve intent_names and modify dispatch semantics. Currently in force: converse and response (both per OVOS-CONVERSE-1, PR #25), both using dispatch suppression (the reserving spec's match-phase or delivery-path code emits the dispatch directly; the orchestrator MUST NOT re-dispatch on the returned Match). Renumbers prior §7.3 (in-process equivalence) to §7.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ymorphism; drop dispatch suppression Three coordinated changes to make the pipeline-plugin model fully uniform and to remove a structural exception that OVOS-CONVERSE-1 required: - §4.2 — drop the "plugin MUST NOT emit during match" prohibition. The match contract is the single obligation: return a Match or null. Plugins MAY emit bus Messages during match if their matching strategy requires it (a converse plugin polling active handlers is the canonical case). The spec retains a SHOULD NOT on session-field mutation during match — declined-match corruption is still real for the few session fields used by later iteration stages, but bus emissions are not policed. - §7.0 (new) — codify identifier polymorphism explicitly. Three component shapes own dispatched handlers: plain skill, plain pipeline plugin with bundled handler, hybrid plugin-skill. For the hybrid case, "if a pipeline plugin registers any intent under OVOS-INTENT-4, that plugin's pipeline_id MUST equal its skill_id" — one identifier, two roles. skill_id conceptually names the voice application; pipeline_id names the matching engine; a component that is both carries one identifier filling both roles. - §7.1 — replace the exclusive "stamp skill_id XOR pipeline_id" rule with two independent stamps. context["skill_id"] applies when owner_id is registered as a skill; context["pipeline_id"] applies when owner_id is loaded as a pipeline plugin. For a hybrid plugin-skill both stamps land and carry the same identifier. - §7.3 — simplify the reserved-intent-name registry. Drop the "dispatch suppression" clause that bent §6's match-then- dispatch flow. A reservation is now a namespace lease only: skills can't register the name, and a pipeline plugin's Match bearing the name dispatches normally per §7 (orchestrator emits <owner_id>:<reserved_name>, handler subscribed to that topic runs). Updated the converse/response entries in the table to reflect this — the converse plugin polls active handlers via whatever wire shape OVOS-CONVERSE-1 defines (no longer the dispatch topic), then returns a Match that the orchestrator dispatches normally. Also updates §4.1 Match.owner_id description to cite §7.0's three-shape model rather than the prior implicit OR. Companion PR: OVOS-CONVERSE-1 (#25) needs a matching restructure to drop its dispatch-suppression dependency and rely on the relaxed §4.2 instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6849af0 to
5cd70e2
Compare
…NVERSE-1) OVOS-CONVERSE-1 (PR #25) claims two session fields under SESSION-1 §2.1's field registry: - active_handlers — recency stack of [owner_id, activated_at] pairs - response_mode — single owner_id string when set, absent otherwise Add both to the §3 informative roster of claimed fields and to the §6 owner-spec list. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Update the forward-reference now that the companion imperative continuous-dialog specification (OVOS-CONVERSE-1, PR #25) is in flight. Names the two session fields it owns (response_mode, active_handlers) and cites the §7 evaluation- order rule that disambiguates the two surfaces for the orchestrator. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CONVERSE-1 §3.4 (PR #25) explicitly cites the metadata-transformer hook as the recommended position for mutating session.active_handlers and session.response_mode. Add this to the §3.3 list of permitted mutations, with the §5.4 cancellation-semantics back-reference for mid-wait holder changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add new error_code 'reserved_name' and a §3.5 cross-reference to OVOS-PIPELINE-1 §7.3's reserved-intent-name registry. The orchestrator MUST reject registration payloads that name an intent_name reserved by a companion specification. Currently reserved: converse and response, both by OVOS-CONVERSE-1 (PR #25). The reservation registry is owned by PIPELINE-1; this spec is the enforcement point at registration time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-turn gap OVOS-CONVERSE-1 (PR #25) fills the multi-turn conversation gap that §7 previously listed as planned work. Update §1.2 stack description to include it, and drop the §7 gap entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 v1: prescriptive session-carrier spec
Formalizes Message.context.session as a versioned carrier. Lists only
the fields already claimed by normative specifications in flight
(MSG-1 §4: session_id, lang; PIPELINE-1 §5: pipeline; CONTEXT-1 §2:
context; TRANSFORM-1 §5: six *_transformers). Fixes a field-registry
mechanism (§2.1) so future specs can claim further fields without
amending this document.
Prescriptive, not descriptive — fields carried by current
implementations but unclaimed by any spec are explicitly out of scope.
Closes the OVOS-MSG-1 §4 deferral ("to be formalized in a future
session specification").
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1: take ownership of lang + add language-resolution hierarchy
§3 table: lang owner is §3.2 (this spec), not MSG-1.
§3.2 (new): full lang definition — BCP-47, session vs payload
language split, data.lang vs session.lang distinction, and the
binding language-resolution priority order (stt_lang > request_lang
> detected_lang > data.lang > session.lang > deployment default).
Future specs introducing a new language signal MUST declare where
they insert in this order.
§3.3: wire weight (renumbered).
Intro: stripped meta-commentary; SESSION-1 stands on its own.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.2: TTS keys on payload data.lang, not session.lang
TTS narrates already-produced text whose language was fixed at
render time. session.lang is the fallback only when the payload
carries no language signal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.2: language signals as session-scoped fields, optional consolidation
Adds stt_lang, request_lang, detected_lang as session fields
alongside lang. Each has a normative meaning (what kind of language
signal it carries, who populates it). Consolidation into a single
value for an operation is left to the consumer / orchestrator and is
stage-dependent — the spec describes shape, not policy. §3.2.5 lists
a sensible default ordering as informative guidance only.
data.lang remains per-payload and is explicitly NOT a session field;
consumers reading payload content language read data.lang directly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.2.2: secondary_langs field — ordered fallback pool
Adds secondary_langs as a session field: BCP-47 array of additional
languages the participant speaks/understands, ordered by preference,
not including lang. Three normative use cases called out:
constraining lang-detection predictions to the candidate set;
fallback selection when lang cannot be served; gating outputs in
unrecognized languages.
Hint not an authorization boundary — consumer MAY ignore.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.2.4: request_lang is an emitter-reported hint, not an override
Reframes request_lang per design clarification: it carries the
language the emitter expected/reported at emission time (e.g. the
language assigned to the triggering wake word in a multi-wakeword
setup), not an authoritative pin.
User may speak a different language than the emitter expected;
downstream stages MUST NOT treat request_lang as a guarantee and
MUST NOT override contradictory stt_lang / detected_lang values on
its strength alone. May be used as a prior / tie-breaker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.2.3: tts_lang field — output preference independent of input
Adds tts_lang as a session field carrying the participant's
preferred output language. When omitted, assistant replies in input
language (status quo). When set, text-rendering stages SHOULD render
in tts_lang if capable, otherwise fall back to secondary_langs / a
translation transformer.
Does not influence TTS voice directly (voice keys on data.lang per
§3.2.1) — but determines what data.lang the upstream renderer sets,
so the cascade is single-preference-controls-all-output-stages.
Renumbers §3.2.4–§3.2.8 to accommodate.
Simplifies the bidirectional-translation transformer use case from
'always translate I/O' to 'translate post-hoc only when renderer
cannot natively serve tts_lang'.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.2.3: rename tts_lang → output_lang
Field affects every text-rendering stage (dialog, prompt, response
composition, GUI text), not just TTS. output_lang reflects the
broader scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §2.5: tighten omission-as-single-deferral-mechanism with RFC 2119
Adds explicit MUSTs: producer MUST defer by omitting; consumer MUST
fill with its deployment default. Spec provides no other deferral
surface (no null, no sentinel, no unset Message). Closes the
implicit-normative-prose gap flagged in the review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1: rename context field to intent_context (CONTEXT-1 rename)
* SESSION-1 §3.3, §3.4: claim blacklisted_skills, blacklisted_intents, site_id
Three new session fields registered in §3:
- blacklisted_skills / blacklisted_intents (§3.3) — per-session
negative pipeline filters. Pipeline plugins SHOULD honour them;
internal handling of would-match-but-blacklisted candidates is
unspecified. The orchestrator MUST act as backstop and filter any
non-conformant plugin's returned match, treating a filtered match
as a decline.
- site_id (§3.4) — opaque group identifier. Field slot reserved in
the registry to prevent future collision; no consumer is defined
by SESSION-1 and no value (including "unknown") carries reserved
meaning.
§4.1 default-materialization rule extended to forbid populating any
of the three new fields when synthesizing a default session.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.3: require fully-qualified <owner_id>:<intent_name> for blacklisted_intents
intent_name is only unique within an owner. Allowing a bare
intent_name form would let a single entry silently denylist every
same-named intent across every skill and pipeline plugin in the
deployment — a sharp footgun. Restrict the field to the dispatch-topic
shape <owner_id>:<intent_name>.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.3: note layer-2 use of denylists for multi-tenant authorization
Informative paragraph: the denylists composed with session_id /
source / destination give a layer-2 substrate (HiveMind being the
canonical example) the authorization surface needed for multi-tenant
setups, without SESSION-1 itself defining an authorization model.
The §4 propagation rule plus the orchestrator backstop of §3.3 mean
the policy enforces end-to-end through the single-flip routing
model with no per-hop re-authorization.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.3: drop HiveMind name from layer-2 paragraph
Normative spec bodies stay implementation-agnostic — concrete
projects belong in APPENDIX.md, not in the spec text. The paragraph
now refers to "a layer-2 system (per OVOS-MSG-1 §3.4 / §4.4)"
abstractly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1: move denylist semantics to PIPELINE-1; add blacklisted_pipelines slot
Per the registry pattern §3 already follows (pipeline → PIPELINE-1,
intent_context → CONTEXT-1, *_transformers → TRANSFORM-1), the
denylist fields' semantics belong in their owner spec, not in
SESSION-1. Runtime filtering of match results is PIPELINE-1's
concern.
- Drop §3.3 body (Negative pipeline filters) entirely. The
SHOULD/MUST plugin contract, orchestrator backstop, and informative
layer-2 paragraph move to PIPELINE-1 §5 (separate PR).
- Update §3 registry: blacklisted_skills / blacklisted_intents now
point at OVOS-PIPELINE-1 §5 as owner.
- Add third row blacklisted_pipelines (orchestrator skips them
during iteration); also owned by PIPELINE-1 §5.
- Renumber §3.4 site_id → §3.3, §3.5 Wire weight → §3.4.
- Reword §3.3 site_id paragraph to drop "current OVOS code"
reference per the spec-bodies-stay-implementation-agnostic rule.
- Extend §4.1 default-materialization forbidden-fields list with
blacklisted_pipelines.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1: fix internal cross-refs and producer override-field list
- §3.2 preamble: §3.2.5 → §3.2.7 (the informative consolidation section).
- §3.2.5 request_lang body: §3.2.3 → §3.2.4 (stt_lang) and §3.2.5 → §3.2.6 (detected_lang).
- §3.2.3 output_lang body: data.lang reference §3.2.1 → §3.2.8 (the per-payload section, not the session-preference lang).
- §6 producer-MUST-NOT list: the per-component override enumeration named the field as "context"; the field is intent_context. Extended the list to include all current override fields (the three blacklists and site_id were missed when they were added).
- §6 trailing pointer §3.3 → §3.4 (Wire weight, after the earlier renumbering).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §4.1: back-cite MSG-1 §4.3 'any device-local fields' permission
Audit B7: MSG-1 §4.3 lets a materialized default carry 'any
device-local fields the implementation chooses'; SESSION-1 §4.1
narrows that for the §3 closed set. Not a contradiction (later spec
wins) but the relationship was implicit. Added an explicit sentence
naming the narrowing and a closing sentence noting fields outside
the §3 closed set remain governed by MSG-1 §4.3 alone.
Avoids touching MSG-1 itself (which would require a version bump
for what is effectively a back-cite).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3.4: promote wire-weight clause to canonical SHOULD-omit rule
Establish §3.4 as the single canonical statement of the
omit-when-wire-equivalent-to-omission rule for the entire spec
family. Three named cases:
1. session_id == 'default' — SHOULD omit, since absent / {} /
'default' are all wire-equivalent per §3.1.
2. Per-component override matching the deployment default —
SHOULD NOT populate (existing rule, preserved).
3. Empty array on a list-valued override — wire-equivalent to
omission, SHOULD omit. Covers blacklists, *_transformers,
pipeline.
The rule is SHOULD, not MUST: redundant default-valued fields
are non-optimal but conformant. Other specs claiming fields via
§2.1 inherit this rule without restating it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3, §4.1: register six blacklisted_*_transformers fields
Reserve the six per-type transformer denylist slots in the §3
registry, all pointing at OVOS-TRANSFORM-1 §5.2 as owner. Parallel
to the existing six *_transformers chain-override fields owned by
OVOS-TRANSFORM-1 §5.1; completes the per-type partition.
§4.1 default-materialization forbidden list extended with the six
new fields.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3, §6: register active_handlers and response_mode (OVOS-CONVERSE-1)
OVOS-CONVERSE-1 (PR #25) claims two session fields under SESSION-1
§2.1's field registry:
- active_handlers — recency stack of [owner_id, activated_at] pairs
- response_mode — single owner_id string when set, absent otherwise
Add both to the §3 informative roster of claimed fields and to the
§6 owner-spec list.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §1, §6: cite OVOS-SESSION-2 for lifecycle (in flight at PR #27)
OVOS-SESSION-2 is now the normative reference for session
lifecycle and resumption semantics that SESSION-1 §1 and §6
previously listed as out-of-scope future work. Replace the
forward-references with explicit cross-refs to SESSION-2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1 §3: response_mode wire type is object (not string)
CONVERSE-1's session.response_mode is a structured object
{owner_id, expires_at, auto_continue} per its §2.2 (after the
OVOS-SESSION-2 §2.4 projection-mandate restructure). The
§3 roster row previously listed it as 'string' from the
original per-dispatch-marker design; sync to the current type.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* SESSION-1: fix MUST NOT / SHOULD NOT inconsistency in conformance §6
§3.4 body already states SHOULD NOT for emitting deployment-default
values. The producer MUST NOT in §6 conformance was stricter than
intended — a producer that cannot determine the deployment default
is non-optimal but conformant. Add a clarifying parenthetical.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* SESSION-1: fix §2 subsection ordering (2.5→2.1), bad MSG-1 §4.3 cross-refs, MUST NOT/SHOULD NOT inconsistency, active_handlers wire type
* SESSION-1: update active_handlers/response_mode wire types to match CONVERSE-1 simplification
* SESSION-1: update active_handlers wire type to array of {id, activated_at}
Reflects CONVERSE-1 change restoring activation timestamps.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* SESSION-1 §2.2: drop propagation/scope columns; state invariants in prose
All session fields propagate unchanged (MSG-1 §4) and are
session-scoped by definition. No use case exists for non-propagating
or per-message fields; the machinery introduced error handling
and edge cases without benefit.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* SESSION-1 §2.2: remove propagation/scope steps; update stale references
Steps 3 (propagation) and 4 (scope) dropped from the claim checklist.
All session fields propagate unchanged and are session-scoped by
definition — no per-field override mechanism exists. Updated §4
propagation prose and §6 conformance checklist to match.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* SESSION-1: final review fixes
- §3.1: clarify "default" means "interact with device-local session",
not "originated from device"; document legitimate remote-client
impersonation pattern (remote control, home automation); layer-2
auth concern, not spec's to gate
- §3.2.2: secondary_langs MUST NOT contain lang "at time of emission"
- §3.2.7: replace single priority chain with per-stage guidance;
informative chain was wrong for TTS (should key on data.lang, not
request_lang)
- §3.3 site_id: replace placeholder text with concrete definition;
primary consumer is routing/output-locality policy; MUST NOT be
overwritten by forwarding components
- §4: add SESSION-2 §2.6 cross-ref to mutation permission bullet
- §4.1: make materialization exclusion rule abstract (no hardcoded
field names); durable against future registry additions
- §6: tighten producer propagation rule to allow mutations at
permitted boundaries (SESSION-2 §2.6)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: OVOS-PIPELINE-1 — pipeline-plugin abstraction (draft) Defines the orchestrator and the pipeline-plugin abstraction: opaque-pipeline_id black boxes the orchestrator iterates in session.pipeline_stages order per utterance, first-match-wins. Plugins expose one operation — match(utterance, session) → Match | None, side-effect-free — and are otherwise black boxes. The orchestrator handles dispatch, notifications, and terminal events. Dispatch topic is <owner_id>:<intent_name> where owner is either a skill_id (skill-owned handler) or a pipeline_id (plugin-bundled handler). From outside, skills and plugin-bundled handlers are indistinguishable. Utterance-layer events: - recognizer_loop:utterance (entry) - ovos.intent.matched (positive match notification) - ovos.utterance.cancelled (transformer cancellation) - complete_intent_failure (no plugin claimed) - ovos.utterance.handled (universal end-marker) Handler-lifecycle trio: - ovos.intent.handler.start / .complete / .error Transformer chain: pre-pipeline modification or cancellation. Per-plugin behavioural contracts (converse, fallback, etc.) are out of scope — plugins are black boxes; each defines itself. The spec body is timeless: no mycroft references, no implementation-code citations, no "where this differs from current OVOS" appendix in the spec itself. Current-OVOS context and divergence catalogues belong in APPENDIX.md (covered in a separate commit). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * pipeline: keep session.pipeline; constrain owner_id colon rule - Revert field rename: keep `session.pipeline` (not `pipeline_stages`). The legacy name stays; no need to break observers for a clarity-only edit. - Dispatch topic `<owner_id>:<intent_name>` (§7): clarify that the split is at the FIRST `:`. skill_id and pipeline_id MUST NOT contain `:`; intent_name MAY contain further `:` so handlers can namespace dispatched topics inside their own surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * pipeline §7: forbid `:` in skill_id / pipeline_id / intent_name Simpler than first-colon-wins. The dispatch topic `<owner_id>:<intent_name>` now contains exactly one `:`, so the split is trivially unambiguous and there is no edge case to specify. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1: release as v2 (incompatible with current OVOS) Per the draft-stage versioning policy, v1 is reserved for content drop-in compatible with current OVOS. PIPELINE-1 adds the orchestrator passive registration index and normalizes the universal `ovos.utterance.handled` end-marker across all terminal paths (current workshop misses it on the error path) — both require OVOS-side changes, so the first release ships as v2. Also update the CHANGELOG bullet that still referenced the defunct `session.pipeline_stages` rename — kept as `session.pipeline`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * pipeline: drop transformer chain (out of scope); refine V2 rationale Two related cleanups: 1. **Remove the transformer chain from the normative spec.** Transformers are pre-pipeline message modifiers — they don't match, they don't dispatch, they only mutate the message or cancel it. Their loading/ordering/contract is a separate concern and doesn't need to ride along with the pipeline plugin spec. §10 deleted; §6.1 flow simplified to two terminal paths (matched / no-match); §6.4 terminal-events table loses the cancelled row; §9.3 (`ovos.utterance.cancelled`) deleted and §9.4–§9.6 renumbered to §9.3–§9.5; §11 conformance transformer block deleted and §11 renumbered to §10. Non-goals list updated to explicitly exclude any pre-pipeline utterance- transformer chain. 2. **Refine the V2 rationale** in the CHANGELOG to match the V0/V1/V2 framing: the trigger for V2 is the handler-lifecycle rename (mycroft.skill.handler.* → ovos.intent.handler.*) which actively breaks observers of the legacy names. The passive registration index and the universal `ovos.utterance.handled` end-marker would have been V1-compatible on their own — missing them degrades experience (empty introspection, missed end-marker on workshop's error path) but doesn't break V0 producers/consumers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * README: PIPELINE-1 reading-order — host-side → orchestrator-side; drop transformers mention * scope: drop APPENDIX/CHANGELOG/README; PR #11 is pipeline.md only per 1-file rule * OVOS-PIPELINE-1: orchestrator may be split across cooperating processes §2 — the orchestrator is the logical role; it MAY be implemented as a single process or as multiple cooperating processes (a natural split runs audio-input / utterance-handling / audio-output as separate services). From the spec's perspective those processes together are "the orchestrator"; the split is a deployment / containerization choice. Pipeline plugins, the loaded-plugin set, and the match contract live in the orchestrator process that implements the utterance lifecycle (utterance-handling under the audio-boundary split). Generic voice-OS framing (no "current OVOS does X" wording in the spec body). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1: SESSION-1 / MSG-1 hoists, trio MUST, per-pipeline introspection §3 pipeline_id: drop colon-rule restatement; reference MSG-1 §2.1.1. §5 session.pipeline: drop field restatement; reference SESSION-1 §2.1 claim and §2.5 deployment-default fallback. Tighten partial- unknown rule (orchestrator MUST NOT fall back to deployment default merely because one identifier is unknown). §7 dispatch topic: drop colon-rule restatement; reference MSG-1 §2.1.1. §8.1 handler trio: tighten from SHOULD to MUST — handler MUST emit exactly one terminal event (complete / error). start stays SHOULD. Reason: §9.5 universal end-marker and §8.3 timeout bookkeeping depend on terminal event being deterministic. §10 (new): per-pipeline_id intent introspection. Pull-query topic ovos.pipeline.<pipeline_id>.intents.list with scatter-response pattern. No aggregate query — consumers walk per-pipeline. Pull-query is source of truth; load-time broadcasts are MAY, consumers MUST NOT rely on them. §11 (renumbered) conformance: handler MUST emit terminal event (was SHOULD); pipeline plugin MUST respond to per-pipeline_id introspection queries; orchestrator MUST NOT synthesize trio events. Non-goals: session shape moved to SESSION-1. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §8: handler-trio is orchestrator-owned, not handler-owned Per design clarification: third-party handler code carries no obligation under this specification. The orchestrator wraps every handler invocation and emits the trio itself — start before the call, complete on normal return, error on exception or timeout. §8.1: trio MUSTs now bind the orchestrator. No handler-side participation required. §8.3: rename 'Orchestrator timeout' → 'Handler timeout'. On timeout the orchestrator emits .error (it owns the topic), then ovos.utterance.handled. Drops the prior 'orchestrator MUST NOT synthesize .error' rule since the orchestrator now owns it unambiguously. §11 handler section: replaced with a 'no normative obligation' clause. The spec binds the orchestrator that invokes the handler, not the handler. Also addresses the workshop utterance.handled asymmetry: workshop acts as orchestrator-ish wrapper but didn't emit the trio. Under this revision the wrapper IS responsible — that's the right ownership. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §4: state utterance argument shape explicitly at the plugin boundary A plugin needs the shape contract without having to read TRANSFORM-1 §3.2 to infer it. Now: 'a non-empty list of candidate strings, may have been modified by utterance-transformer chain, all in the same language, no particular order, plugin chooses how to weight'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §9.1: de-prescribe recognizer_loop:utterance topic name Entry-topic name is deferred to a separate spec covering audio-input ↔ assistant-core wire contracts. Current OVOS uses recognizer_loop:utterance for compatibility; conformant orchestrators MAY subscribe to that name in the interim. What IS normative is the behaviour after entry: §6 lifecycle, §9.5 end-marker, §§7-8 obligations. The entry name and payload shape are not. Internal refs updated to 'entry topic (§9.1)' style throughout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §10: clarify split-orch ownership of per-pipeline_id queries * PIPELINE-1 §7.1: orchestrator stamps context.skill_id on dispatch Per design clarification: the dispatch topic <skill_id>:<intent_name> implicitly tells the orchestrator which skill_id to stamp on context. By stamping at dispatch time, all skill emissions derived from the dispatch (via forward/reply) inherit the correct skill_id — INTENT-4 §3.1's loader-side enforcement becomes automatic for the dispatch path. Any drift between context.skill_id and the dispatch <owner_id> on a skill emission is non-conformant and detectable. Plugin-bundled handlers (owner_id is a pipeline_id, not a skill_id) do NOT get skill_id stamped — they identify via pipeline_id, per the existing dispatch polymorphism. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §5: claim the three blacklist session fields with full semantics SESSION-1's §3 registry pattern: SESSION-1 fixes the field slot, the owner spec defines semantics. The denylist fields registered in SESSION-1 §3 point here; this commit writes the semantics they need. §5 restructured into a multi-field section: - §5.1 session.pipeline (the existing body, unchanged) - §5.2 session.blacklisted_pipelines — orchestrator-only filter, exists because pipeline is a positive whitelist and an origin that doesn't know the deployment default needs a negative knob - §5.3 session.blacklisted_skills — two-tier (plugins SHOULD honour, orchestrator MUST backstop). Plugin internal handling of would-match-but-blacklisted candidates is explicitly unspecified - §5.4 session.blacklisted_intents — same two-tier shape. Entries MUST be fully-qualified <owner_id>:<intent_name>; bare intent_name is rejected to prevent silent cross-skill collision - §5.5 Composition — effective pipeline = (session.pipeline OR deployment default) minus blacklisted_pipelines, computed once per utterance. blacklisted_skills/_intents apply per Match during iteration. Explicit pipeline wins; blacklisted_pipelines is the ergonomic alternative when the origin lacks default knowledge - §5.6 (informative) Layer-2 substrate authorization — denylists + source/destination + session_id give a layer-2 substrate the authorization surface for multi-tenant setups, riding the single-flip routing model with no per-hop re-authorization §1 scope updated to list the four session fields owned here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §5: fix composition rule — policy overrides preference, not alternatives Previous wording in §5.5 framed session.pipeline and blacklisted_pipelines as alternatives ("explicit pipeline wins; blacklist is the ergonomic alternative"). That misread the model. Correct layering: - session.pipeline is the **preference channel** — any session origin (local, remote, layer-2-attached, programmatic) MAY populate it as a request. No authorization implied. - The orchestrator narrows the request to what is loaded (availability) and what policy permits (the denylists). - Denylists override preference: a pipeline_id requested in session.pipeline and also listed in blacklisted_pipelines is dropped. Same per-Match for blacklisted_skills/_intents. §5.5 rewritten around three explicit stages: preference, availability, policy. Empty effective pipeline → no-match, without silent fallback to the default-session pipeline (a fallback would let a policy-rejected request pull in an ordering nobody approved). §5.1 reframed as preference channel; clarified that the "deployment default" is in practice the pipeline configured for the reserved session_id == "default" session (OVOS-SESSION-1 §3.1), which the orchestrator owns. §5.2 reframed as policy channel; explicit "MUST NOT be invoked even if requested in session.pipeline". §5.6 reframed around the intended client/layer-2 split: clients request via session.pipeline; layer-2 substrates that own the session enforce via the denylists. The two roles use different fields, and §5.5 layering makes enforcement automatic. Also scrubbed example pipeline_ids ("padatious-*", "adapt-*") that named real engines, replacing with generic "template-*" / "keyword-*" per the no-named-projects rule for spec bodies. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1: claim context['pipeline_id'] for plugin self-identification; fix §5.4 lang note; tighten §9.1 lang fallback Three fixes from the cross-spec audit: - §3.1 (new): plugins MUST stamp Message.context['pipeline_id'] on every emission, parallel to OVOS-INTENT-4 §3.1's context['skill_id'] rule for skills. This gives downstream specs (CONTEXT-1 §5.2 attribution of plugin-emitted ovos.context.set; future telemetry / audit consumers) a wire-level source for plugin attribution. Includes the emitter vs subject distinction (context['pipeline_id'] vs data['pipeline_id']), reserved-key precedence on derivations, mutual exclusion with context['skill_id'] (a Message carries one or the other, never both), and orchestrator-side loader enforcement. - §5.4 blacklisted_intents: added paragraph noting the field is language-agnostic. INTENT-4 §3.2 keys intent identity on (skill_id, intent_name, lang); blacklisted_intents keys only on (owner_id, intent_name) so a single entry denies both en-US and de-DE registrations of the same intent. Per-language denial is expressed via session.lang narrowing the matchable set. - §9.1 entry-topic lang field: previously anchored a normative MUST on OVOS-SESSION-1 §3.2.7 — which is explicitly informative consolidation guidance. Rewrote to name two normative sources (session.lang from §3.2.1, then deployment default) and note that the §3.2.7 cascade applies to downstream stages, not to this entry-point field. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §3.1: drop spurious INTENT-4 §3.1.4 sub-section ref INTENT-4 §3.1's emitter-vs-subject treatment is in an unnumbered subsection; cite the parent §3.1 to avoid drift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §3.1: drop strip-and-mutual-exclude rule; allow identity keys to coexist Reviewer feedback: the prior 'Reserved-key precedence' paragraph required plugins emitting via forward/reply to strip an inherited context['skill_id'] and forbade the two keys appearing together. That conflicts with the natural MSG-1 §5 derivation semantics (forward preserves context unchanged) and breaks chain traceability — a plugin emission flowing from a skill dispatch legitimately carries both keys: the skill it dispatched from AND the plugin actually emitting. Reworked the paragraph to acknowledge coexistence: forward/reply preserve upstream identity stamps; the emitting plugin additionally stamps its own pipeline_id. When an attribution consumer needs to pick one, it uses most-specific identity wins, codified in CONTEXT-1 §5.2. Also reworded 'is the emitter' to 'is the plugin's self-attribution' since 'emitter' is ambiguous in chains crossing multiple component types. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §3.1: split stamp rule into originate vs modify-in-place; update transformer cross-ref Aligning with the uniform MUST-stamp-on-originate-or-modify rule adopted across INTENT-4, PIPELINE-1, and TRANSFORM-1. The plugin binds the context['pipeline_id'] stamp obligation both on free-form bus emission (originate) and on in-place mutation of a Message it processes (modify in place). Updated the coexistence paragraph to reference the new TRANSFORM-1 §1.3 per-type keys (six <type>_transformer_id slots, not a single generic transformer_id) and to point to the lifecycle-position precedence in CONTEXT-1 §5.2. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §3.1: stamp rule covers derivations placed on the bus by the plugin User feedback: a Message.reply or .response derivation that the plugin performs and emits IS a plugin emission — the resulting Message-on-wire is caused by the plugin and MUST carry context['pipeline_id']. Earlier wording carved derivations out ('a fresh emission, not a forward/reply/response derivation'), which was wrong. Rewrote the stamp rule around 'places on the bus' covering both fresh emissions and derivations (forward, reply, response) the plugin performs and emits. The derivation mechanism is irrelevant; what binds is that the plugin caused a Message to appear on the bus. Modify-in-place rule preserved as a separate clause covering context/data/session mutations that don't themselves emit. Also updated the cross-spec ref to the new <type>_transformer_ids (plural-list) shape from TRANSFORM-1 §1.3. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §5.2 §5.3 §5.4: drop [] != omission distinction; align with SESSION-1 §3.4 SHOULD-omit Per design decision (option B): the deliberate '[] is explicit none-denied, distinct from omission' semantics in the blacklist fields are dropped. [] is now wire-equivalent to omission for all three denylist fields; both resolve to the deployment default at consumption per SESSION-1 §2.5. Producers SHOULD omit empty arrays rather than emit them. The use case the distinction served — a session explicitly clearing a deployment-default denial via [] — is rare enough to not warrant the footgun. Layer-2 substrates wanting to grant 'bypass deployment default' permissions to a peer should use a smaller deployment default in the first place. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §3.1, §6.1, §6.2: forward exemption + post-match-pre-dispatch window + circuit-breaker Three audit findings actioned: - §3.1 stamp rule: pure-forward propagation exempt. .reply and .response are authorial actions that MUST stamp pipeline_id; .forward is propagation and MUST NOT overwrite an inherited context['pipeline_id']. Preserves upstream attribution chains when a downstream plugin merely propagates an earlier plugin's emission. Authors wanting to claim authorship use .reply, .response, or fresh emit. - §6.1 lifecycle pseudocode: added the post-match-pre-dispatch window where CONTEXT-1 §5.3 sanctions engine-side context mutation and TRANSFORM-1 §3.4 inserts the intent-transformer chain. Also annotated where the utterance, metadata, dialog, and TTS transformer chains plug in. A reader of PIPELINE-1 alone no longer gets a wrong picture of the lifecycle ordering. - §6.2 plugin-exception handling: added a circuit-breaker clause. Orchestrators SHOULD track per-plugin exception counts and SHOULD drop a plugin from the session's effective pipeline after a deployer-tunable threshold (typically three) of consecutive failures. Optional informative ovos.pipeline.dropped diagnostic event named. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §9.1: rename entry topic recognizer_loop:utterance -> ovos.utterance.handle The Mycroft-era recognizer_loop:utterance fails the naming conventions of OVOS-MSG-1 §2.1.2 on three counts: - ':' used as a segment separator, where ':' is reserved for the <owner_id>:<intent_name> dispatch topic shape (MSG-1 §2.1.1). - Leading segment names an implementation role ('recognizer loop', the Mycroft audio-input service) rather than a stable assistant root. - Does not pair with the past-tense terminal event ovos.utterance.handled per the request/terminal verb-pairing convention of MSG-1 §2.1.2(d). Renamed to ovos.utterance.handle: dot-separated hierarchy, stable ovos. root, imperative-mood request verb pairing with handled. §9.1 now prescribes the topic name directly; removed the 'name deferred to a future spec' language. §6.1 lifecycle diagram and other in-spec entry-topic refs updated. Migration cost acknowledged: a transitional deployment MAY subscribe to both names; the legacy topic has no normative status under this spec. Depends on MSG-1 v2 (spec/msg1-v2-topic-naming) for the §2.1.2 naming conventions cited. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §9.1: drop migration meta-commentary Remove the 'Migration from recognizer_loop:utterance' paragraph. The spec now states only the current normative rule (ovos.utterance.handle is the entry topic). Historical predecessor mapping lives in APPENDIX §6.7. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §4, §9.1: match signature takes explicit optional lang; orchestrator MUST NOT synthesize §4 match contract: signature becomes match(utterance, lang, session). lang is OPTIONAL, sourced from Message.data.lang. Orchestrator passes it through when present; MUST NOT synthesize from session.lang or any §3.2 signal when absent. Plugin that needs language and receives lang=None MAY consult session for §3.2 signals or apply its own policy. Same shape and rationale as TRANSFORM-1 §3.0. §9.1 entry-topic lang field: rewritten. Previous wording said the orchestrator MUST fall back to session.lang — that was wrong; the orchestrator MUST NOT fabricate a value. data.lang is present only when the producer authoritatively knows the content language; its absence is a faithful 'unknown' signal that consumers resolve per their own stage-appropriate policy. §6.1 pseudocode and §11 conformance refs updated for the new match signature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1: post-audit fixes — Match.lang, empty-list short-circuit, cancellation terminal row, conformance entry-topic Four audit findings actioned: - §4.1 Match shape: add optional 'lang' field. Resolves the TRANSFORM-1 §3.0 contradiction where intent transformers were told to read Match.lang as the language source, but Match didn't declare the field. Authoritative-by-construction for a plugin that received a non-None lang parameter; plugins that determine lang otherwise (multilingual matcher, content- language detector, hard-coded engine) MAY set it freely; absent when the plugin doesn't commit to a language. - §6.2 empty-list short-circuit: when the utterance-transformer chain returns an empty list, the orchestrator MUST NOT invoke any pipeline plugin (match()'s input contract is non-empty). Proceeds directly to complete_intent_failure. Cancellation context (TRANSFORM-1 §8.1) on the same return takes precedence over no-match. - §6.4 terminal-events table: cancellation added as a third row, cross-referencing TRANSFORM-1 §8.2. The 'every utterance ends in handled' invariant remains intact. - §11 conformance: subscribe to ovos.utterance.handle directly, dropping the stale 'name deferred; current deployments use recognizer_loop:utterance' bullet. §9.1 has already prescribed the new name; the conformance bullet was contradicting its own spec. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §7.3: reserved-intent-name and dispatch-suppression mechanism Add a §7 subsection codifying the registry mechanism by which other specs reserve intent_names and modify dispatch semantics. Currently in force: converse and response (both per OVOS-CONVERSE-1, PR #25), both using dispatch suppression (the reserving spec's match-phase or delivery-path code emits the dispatch directly; the orchestrator MUST NOT re-dispatch on the returned Match). Renumbers prior §7.3 (in-process equivalence) to §7.4. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §4.2, §7.0, §7.1, §7.3: relax match contract; explicit polymorphism; drop dispatch suppression Three coordinated changes to make the pipeline-plugin model fully uniform and to remove a structural exception that OVOS-CONVERSE-1 required: - §4.2 — drop the "plugin MUST NOT emit during match" prohibition. The match contract is the single obligation: return a Match or null. Plugins MAY emit bus Messages during match if their matching strategy requires it (a converse plugin polling active handlers is the canonical case). The spec retains a SHOULD NOT on session-field mutation during match — declined-match corruption is still real for the few session fields used by later iteration stages, but bus emissions are not policed. - §7.0 (new) — codify identifier polymorphism explicitly. Three component shapes own dispatched handlers: plain skill, plain pipeline plugin with bundled handler, hybrid plugin-skill. For the hybrid case, "if a pipeline plugin registers any intent under OVOS-INTENT-4, that plugin's pipeline_id MUST equal its skill_id" — one identifier, two roles. skill_id conceptually names the voice application; pipeline_id names the matching engine; a component that is both carries one identifier filling both roles. - §7.1 — replace the exclusive "stamp skill_id XOR pipeline_id" rule with two independent stamps. context["skill_id"] applies when owner_id is registered as a skill; context["pipeline_id"] applies when owner_id is loaded as a pipeline plugin. For a hybrid plugin-skill both stamps land and carry the same identifier. - §7.3 — simplify the reserved-intent-name registry. Drop the "dispatch suppression" clause that bent §6's match-then- dispatch flow. A reservation is now a namespace lease only: skills can't register the name, and a pipeline plugin's Match bearing the name dispatches normally per §7 (orchestrator emits <owner_id>:<reserved_name>, handler subscribed to that topic runs). Updated the converse/response entries in the table to reflect this — the converse plugin polls active handlers via whatever wire shape OVOS-CONVERSE-1 defines (no longer the dispatch topic), then returns a Match that the orchestrator dispatches normally. Also updates §4.1 Match.owner_id description to cite §7.0's three-shape model rather than the prior implicit OR. Companion PR: OVOS-CONVERSE-1 (#25) needs a matching restructure to drop its dispatch-suppression dependency and rely on the relaxed §4.2 instead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §4.1, §4.2, §6: Match.updated_session as the match-phase session-mutation channel Per current ovos-core (sess = match.updated_session or SessionManager.get(message)), a plugin's match operation MAY mutate session state and return the new snapshot via the optional Match.updated_session field. The orchestrator then uses that snapshot for the dispatch and every downstream stage; a declining plugin returns null, so its experimental mutations are discarded at the plugin boundary and never reach later plugins in iteration. Updates: - §4.1 Match table: add updated_session optional field - §4.2: explicit mechanism + 'plugins that mutate session in place without populating updated_session are non-conformant' - §6 flow diagram: insert 'session = match.updated_session or session' immediately after a non-null match, before the post-match-pre-dispatch window This is what makes match-phase mutation safe under §6.2 first-match-wins: only the claiming plugin's session changes land, declined plugins' mutations are scoped to their match call and discarded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §7.0, §7.1: collapse handler-owner shapes; pipeline_id == skill_id always Per design feedback: the prior 'hybrid plugin-skill vs plain plugin with bundled handler' distinction wasn't load-bearing. Collapse to two shapes: - Plain skill: handler reached via skill_id, intents matched by some other plugin via INTENT-4 registrations - Pipeline plugin with bundled handlers: handler reached via pipeline_id == skill_id (the plugin owns its own intent matching and handler dispatch); MUST NOT register its intent_names under INTENT-4 (would create circular dependency) Passive-index registration: a pipeline plugin with handlers SHOULD publish its intent_names via the per-pipeline introspection topic ovos.pipeline.<pipeline_id>.intents.list so deployment-wide tools that enumerate handlers can see them. This is one-way declaration, not INTENT-4 registration. §7.1 stamping simplified accordingly: - context['skill_id'] = owner_id ALWAYS (skill_id is the universal voice-app identity) - context['pipeline_id'] = owner_id WHEN owner is loaded as a plugin (both keys carry the same value for plugin-handlers) §4.1 Match.owner_id description simplified to two shapes. skill_id is conceptually the voice_app_id; pipeline_id is the matching-engine id; a plugin that is both carries one identifier filling both roles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §7.0: pure-matcher plugin shape; plain skill + reserved intent_names Two clarifications to the handler-owner table: - A 'pure-matcher' pipeline plugin shape exists alongside the two handler-owner shapes. Padatious / Adapt (skill-intent matchers) and the OVOS-CONVERSE-1 converse plugin (reserved- intent-name matcher) are pure matchers: they are pipeline plugins per §3 but not handler-owners; their match returns matches whose owner_id is some OTHER component's identity. They have a pipeline_id but no skill_id. - A plain skill may also handle a reserved-intent-name dispatch topic when a companion spec defines one. A skill that implements a converse method subscribes to <own_skill_id>:converse via framework convention, not via INTENT-4 registration (which would be rejected per §7.3). The 'plain skill' row's INTENT-4 path is the normal case; reserved-name framework-convention subscriptions extend it without changing the dispatch shape. Removes 'converse' from the bundled-handler-plugin examples list — the converse plugin is a pure matcher, not a bundled-handler plugin. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §4.2, §6: tighten match-phase scope; explicit null-match path Two clarifications from the review: - §4.2 'plugins MUST mutate via updated_session' explicitly scoped to match-phase only. Handler-time, transformer-time, and MSG-1-direct-mutation pathways for session changes are governed by their own specs and unaffected by §4.2's rule. - §6 flow diagram restructured to show the null-match branch explicitly: 'if match is None: continue # any plugin-side updated_session is discarded'. Makes the F5 review point visible — declined plugins' updated_session never reaches the next iteration. Indentation adjusted accordingly for the subsequent ovos.intent.matched / dispatch / trio / end-marker block (one level shallower now that the inner-if collapsed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1 §2: brief note on SESSION-2 stateless-for-named, owns-default model PIPELINE-1's orchestrator role description predates SESSION-2 and didn't mention the statelessness model. A reader of PIPELINE-1 alone might assume the orchestrator is fully stateful. Add a one-paragraph note pointing to SESSION-2 as the owner of the state-ownership model and stating the working assumption: the orchestrator is stateless for named sessions and holds persistent state only for session_id == 'default'. Editorial; no normative behaviour change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * PIPELINE-1: fix three spec bugs - §11 conformance: remove incorrect "side-effect-free during match" MUST — §4.2 body explicitly permits bus emissions from match - §9.1: fix bad cross-reference §2.1.2 → §2.1 (MSG-1 has no §2.1.2) - §9.5: revert accidental status-field addition; payload MAY be empty Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * MSG-1 §2.1.1: update identifier-constraint wording to per-topic-shape rule (PR #29) * PIPELINE-1: fix 5 audit gaps - §4.4 (new): match-phase timeout SHOULD, parallel to §8.3 handler timeout; timed-out match treated as exception, session unchanged, no bus event - §4.1: Match.utterance made required; plugin that does not track winner MUST use first candidate as fallback; orchestrator forwards verbatim - §7.1: dispatch.data.lang changed to conditional; populated from Match.lang, fallback to entry-topic lang, omitted when neither source provides one; handlers MUST treat as optional - §6.1: clarify dialog/TTS chain timing — ovos.utterance.handled fires at handler completion, not after audio rendering; audio output is fully decoupled from pipeline (chat deployments have no audio output); dialog/TTS chains are causal, not synchronization barriers - §9.3: complete_intent_failure payload table added (utterances, lang, both optional); consistent with other bus-event schemas in §9 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §9.6: define ovos.utterance.speak as NL output exit point Frames PIPELINE-1 as defining the natural-language I/O boundary of the voice assistant: ovos.utterance.handle (entry) and ovos.utterance.speak (exit) are the symmetric NL input and output surfaces. Everything between is pipeline; everything downstream of speak (TTS, audio queueing, display) is out of scope and belongs to the output-path companion specification. - §1: add ovos.utterance.speak to scope bullet - §6.1 flow diagram: add ovos.utterance.speak (×0..N) step; update "(TTS rendering)" label to "(output-path delivery)" to reflect decoupling - §6.1 prose: reference §9.6 by topic name instead of "speak Message" - §6.4 terminal events table: add speak ×0..N to matched-by-plugin path - §9.6 (new): full definition — topic naming rationale, payload schema (utterance required, lang optional), derivation via forward/reply from dispatch (session + skill_id propagation), multiplicity rule (zero permitted for silent handlers), broadcast, output-path out of scope Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: simplification pass — 7 structural fixes 1. Fix §4.3/§4.4 ordering: capture map is §4.3, timeout is §4.4 (was swapped) 2. §3.1 stamp rule: drop modify-in-place paragraph (unenforceable); fix framing — .reply/.response are routing metadata, not authorial actions; only fresh constructions require stamping; all derivations preserve inherited context["pipeline_id"] 3. §3.1 coexistence paragraph: compressed from 4 sentences to 2 4. §7.0: remove "plain skill + reserved intent_names" paragraph → moved to §7.3; remove "passive index registration" SHOULD → moved to §10 intro; closing summary sentence retained 5. §2: drop "plugins are diverse" flavor paragraph; move deployment SHOULD (load at least one INTENT-4-consuming plugin) to §11 conformance under new "A deployment SHOULD" block 6. §5 intro: drop "§5.5 fixes..." navigation hint sentence 7. §11: drop Non-goals block (duplicates §1) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §3.1: rewrite identity stamping to two clear rules Drop all derivation/routing/authorship framing — that belongs in MSG-1. The section now states exactly what is needed: - orchestrator stamps context["pipeline_id"] before each match call - orchestrator stamps context["skill_id"] = owner_id on every dispatch Both fields flow automatically from there; no plugin author policy required. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §3.1: simplify to two sentences Orchestrator stamps pipeline_id on match selection. Handler identity (skill_id) is INTENT-4's rule, applies to bundled-handler plugins identically to plain skills — fully polymorphic, nothing to add here. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §3.1: pipeline_id stamped at dispatch, propagates via MSG-1 Clarify that pipeline_id first appears on the dispatch Message (§7.1), not at match time. MSG-1 derivation semantics carry it through handler emissions with no further action required by the plugin or handler. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: pre-merge fixes - §1: conformance cross-ref corrected to §11 (was §10) - §9 intro: drop hardcoded "five" event count - §7.0: remove named project references from pure-matcher paragraph (golden rule — describe by role, not by implementation name) - §7.1: replace "via forward (MSG-1 §5.1)" with "MSG-1 derivation semantics" — routing mechanics belong in MSG-1, not here; drop redundant skill_id drift paragraph (INTENT-4 §3.1 owns it) - §9.6: replace explicit forward/reply prescription with "derives from the dispatch Message per MSG-1 §5 derivation semantics" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §7.0: drop two-shapes framing; dispatch is uniform The "plain skill vs plugin-with-bundled-handlers" distinction was architectural noise — there is no difference. A match has an owner_id which is the skill_id of the handler. If that is the plugin itself, skill_id == pipeline_id. Same dispatch, same handler obligations, same INTENT-4 rules. Plugin just skipped the registration bus round-trip. - §7.0: rewritten to state the single rule plainly; table and "identifier polymorphism" heading removed - §7.1: pipeline_id stamp rule simplified — always stamped from the producing plugin; no conditional on handler-owner shape - §2: remove "two dispatch shapes" paragraph (no longer needed) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: replace owner_id with skill_id throughout owner_id was an abstract alias for skill_id with no distinct meaning. Now that the two-shapes framing is gone, the abstraction serves no purpose. Every match, dispatch topic, payload field, and prose reference now uses skill_id directly, consistent with INTENT-3/4 and the rest of the spec set. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: reframe as foundational spec; drop INTENT-4 from builds-on INTENT-4 is an optional layer built on top of PIPELINE-1, not a dependency of it. Move INTENT-4 to a "See also" note. Reframe the opening paragraph so the spec presents itself as the NL entry/exit boundary rather than a companion to the intent specs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: rename complete_intent_failure → ovos.intent.unmatched Follows MSG-1 dot-namespaced topic convention. Symmetric with ovos.intent.matched. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §4.4: add latency discipline — defer long-running work to handler Plugins SHOULD return from match immediately and defer expensive work (model inference, network calls) to the handler phase. Canonical example: an LLM plugin can match instantly and generate in the handler. Orchestrator SHOULD surface match-phase duration as an observable metric. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: captures→slots, utterance→utterances param, drop 3 non-normative paragraphs - Match.captures → Match.slots throughout (§4.1, §4.3, §7.1, §9.2, §6.1) - match(utterance,…) → match(utterances,…) in signature, §4 inputs, §11 - Drop naming-convention explanation paragraphs from §9.1 and §9.6 - Drop §4.2 "flow diagram reflects this" cross-ref (diagram already shows it) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §6.1: dialog/TTS transformers run in output layer after .handled They are not part of the pipeline lifecycle; move them outside the flow diagram's break point and correct the prose to say they run just before TTS rendering in the output layer, not inside the dispatch path. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §6.2: trim circuit-breaker; drop ovos.pipeline.dropped topic Collapsed the 10-line circuit-breaker block to 4 lines — the SHOULD discipline is preserved, the over-specified event topic and payload are dropped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §7.1: drop skill_id and intent_name from dispatch payload Both are encoded in the topic (<skill_id>:<intent_name>); repeating them in data is redundant. A handler that needs them splits the topic on ':'. §9.2 ovos.intent.matched keeps them (broadcast, no topic encoding). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §7.1: mandate lang in dispatch payload By dispatch time the pipeline must have resolved a content language. lang is now required in the dispatch data; a match with no lang and no entry-topic lang is treated as declined. Match.lang updated to reflect the same obligation. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: mandate Match.lang — plugin owns language resolution Match.lang is now required. Language resolution is the plugin's explicit responsibility: use the entry-topic lang hint, session signals, or any other policy, then declare the result. A Match without lang is malformed and treated as declined. Dispatch lang is taken directly from Match.lang with no orchestrator fallback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: drop §7.4 In-process equivalence Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1 §6.5: long-running handlers and nested utterance lifecycles Documents the get_response nesting pattern: inner utterance lifecycle runs to completion (including its own .handled) while the outer handler is blocked. Mandates orchestrator concurrency (deadlock otherwise). Clarifies that the per-entry .handled invariant applies independently to each ovos.utterance.handle. Cross-references CONVERSE-1 §5 for the response routing mechanism. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * PIPELINE-1: pre-merge polish pass - §2: fix sentence run-on - §4.2/§9.1/§9.6: remove double blank lines - §6.1: drop "(highlighted)" — not meaningful in markdown - §11 plugin MUST: add lang to the required Match fields - §11 orchestrator MUST: add §6.5 concurrency obligation - §10.1: fix confusing Match.skill_id parenthetical → pipeline_id Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: README — full spec-set refresh for the in-flight stack Update the README to reflect the full spec set landing together: the original intent stack (INTENT-1/-2/-3, MSG-1) plus the in-flight specs (INTENT-4, SESSION-1, SESSION-2, PIPELINE-1, TRANSFORM-1, CONTEXT-1, CONVERSE-1). Changes: - Specification table reorganised into three stacks — intent, bus, orchestrator — each with a one-paragraph narrative. This is the structure APPENDIX §1.2 already uses; the README now mirrors it for consistency. - New 'Where to start' section with four reading-order paths matching common audiences: skill author, plugin author, orchestrator author, architecture surveyor. Addresses the 'no clear entry point' friction first-time readers hit when the set went from 4 to 11 specs. - New 'How this compares to other voice frameworks' section summarising APPENDIX §2's positioning (Home Assistant / hassil, Rasa, Alexa / Dialogflow, Rhasspy / Hermes, Wyoming). Brief; points at APPENDIX for detail. - Reference-implementation section split: ovos-spec-tools covers the intent stack; bus and orchestrator stacks are acknowledged as not-yet-having-ground-up-reference-impl with pointer to APPENDIX §5 divergence catalogue. - New 'Implementation status' section: clarifies the spec-set Draft→stable transition is tracked at #5; intent stack is most aligned with current ovos-core; known gaps cited from APPENDIX §7. - Contributing section adds the one-file-per-PR rule (per AGENTS.md repo convention) and clarifies dev vs master targeting. - Updated draft warning to reference APPENDIX §5 divergence catalogue and link to #5. No normative-spec changes; README and supporting-metadata only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * README: establish voice OS framing; add OS-analogy table Replace "voice assistant ecosystem" opening with "voice operating system" framing. Add "What a voice operating system is" section with OS-analogy table (scheduler, IPC, shared memory, process supervision, loadable modules, syscall ABI) and the portability consequence. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * README: full spec table — three stacks, open PR links Split specs into intent / bus / orchestrator stacks. Add all 11 specs including in-review ones (INTENT-4 #9, INTENT-2 v3 #4, TRANSFORM-1 #20, CONTEXT-1 #18, CONVERSE-1 #25). Add role-based reading order. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… PIPELINE-1 (#14) * docs: APPENDIX — audit-driven corrections (pipeline + registration model) Applies corrections found by auditing claims against actual OVOS source code: 1. **§6.7 enable/disable_intent legacy names corrected** to the real `mycroft.skill.enable_intent` / `mycroft.skill.disable_intent`. 2. **§6.4 direct-bus-subscribe claim broadened** — verified the standard ovos-padatious-pipeline-plugin and ovos-adapt-pipeline-plugin both subscribe directly to registration topics, not just downstream plugins. 3. **§6.4 "side-effects during match" softened** — audit confirms the official match_* methods are already side-effect-free; the skill-activation emit is orchestrator-side, not plugin-side. Rule reframed as forward-looking discipline. 4. **§3 / §4 / §6.4: PIPELINE-1 *refines* the plugin model rather than *introducing* it.** OVOSPipelineFactory, pipeline_plugins dict, _PIPELINE_MIGRATION_MAP, and the official plugin set already exist. PIPELINE-1's actual contribution narrows to: formalizing the contract, `<owner_id>:<intent_name>` polymorphism, universal `ovos.utterance.handled` end-marker, and the renames. 5. **§3 / §4 / §6.4: tier convention is compatible, not a divergence.** From the bus each tier is already a distinct `pipeline_id` in `Session.pipeline`. How a Python plugin class internally serves multiple `pipeline_id`s (one class with match_high/medium/low methods, an orchestrator-side suffix-decoder, separate plugin instances, etc.) is implementation choice the spec does not constrain. 6. **§4 / §6.4: registrations-are-broadcast is compatible, not a divergence.** OVOS already broadcasts registrations on the bus; plugins already subscribe directly. INTENT-4 does not change this — it only renames topics into the `ovos.intent.*` namespace (see §6.7). Migration is a string replacement. What IS new is the orchestrator's passive registration index that backs `ovos.intent.list` / `.describe` — that's added as a separate §6.4 divergence ("new orchestrator responsibility, not a change to existing behaviour"). 7. **§6.6 adds note on engine-specific introspection topics** (`intent.service.adapt.*`, `intent.service.padatious.get`) — plugin-defined surface; spec does not claim authority over them. No spec-body changes; APPENDIX only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: APPENDIX §6.4 — drop the "dissolution" divergence Same logic as the broadcast-registrations correction: the orchestrator already treats every loaded plugin uniformly, and `IntentHandlerMatch.match_type` is an opaque string the plugin chooses — nothing in current code prevents a plugin from setting `match_type = "<pipeline_id>:<intent_name>"` and being dispatched to itself. The `<owner_id>:<intent_name>` polymorphism PIPELINE-1 names is therefore already supported; the spec only writes down a convention current code allows but does not document. Design rationale around the polymorphism stays in §3/§4 — it is useful explicit naming. But it is not a divergence and should not sit in the divergence catalogue. §6.4 now contains a single real divergence: the orchestrator's new passive registration index backing `ovos.intent.list` / `.describe`. Everything else in §6.4 is forward-looking discipline or a workshop bug, not an architectural change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: keep session.pipeline (revert the rename row) PIPELINE-1 now keeps the existing `session.pipeline` field name instead of renaming it to `pipeline_stages`. Drop the §6.2 rename row and revert the prose mentions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §7: note utterance-transformer chain as a deferred spec (out of scope for PIPELINE-1) * APPENDIX §4 / §7: design notes for OVOS-CONTEXT-1 and OVOS-TRANSFORM-1 Per the new "dedicated APPENDIX PR" policy, consolidating the prior-art and design-deviation notes from the OVOS-CONTEXT-1 (PR #18) and OVOS-TRANSFORM-1 (PR #20) work into this PR. Those spec PRs are now scoped to their own spec files only; the discussion / cross-spec touchups / in-tree prior art all live here. Adds to §4 Design rationale: - "Intent context (CONTEXT-1)" — the Adapt-only origins, the two-scope (private/shared) formalization, jurebes / nebulento / palavreado as prior art for excludes_context, the engine-side §5.3 mutation pathway resolving the PIPELINE-1 §4.2 contradiction. - "Transformer plugins (TRANSFORM-1)" — the architectural- pattern framing, intent transformers as the system-typing home, the nine concrete in-tree plugins as prior art, the ascending-vs-descending priority deviation called out, cancellation alignment with existing plugin convention, and the language disambiguation hierarchy mirroring current ovos-core code paths. Removes from §7 Known gaps: - "Intent context" bullet (formalized in CONTEXT-1). - "The utterance-transformer chain" bullet (formalized in TRANSFORM-1). * APPENDIX: SESSION-1 rationale; introspection patterns; revised divergences §4 — new 'Session (SESSION-1)' rationale subsection: why it exists, prescriptive-not-descriptive scope, omission-as-deferral semantics, four language signals. §4 'Transformer plugins' — language-disambiguation note updated: hierarchy moved out of TRANSFORM-1 to SESSION-1 §3.2; transformer types now just named as natural producers of signals, consolidation is consumer's stage-dependent choice. §6.4 architectural divergences — add: handler-trio ownership shifted to orchestrator (third-party handler code carries no obligation); per-pipeline_id intent introspection (PIPELINE-1 §10); CONTEXT-1 scope discriminator. Update ovos.utterance.handled note to reflect the trio-ownership shift (workshop fix is now in the wrapper, not the handler). §6.5.1 (new) — introspection-patterns table comparing INTENT-4, PIPELINE-1, CONTEXT-1, TRANSFORM-1 surfaces. Three shared properties (pull-query is source of truth, no completeness signal, per-process slices under split orchestrators). Notes naming-convention inconsistency as candidate follow-up. §6.6 — remove obsolete 'session shape deferred' note; replace with SESSION-1 ownership statement. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: update §6.5.1 topic-naming (resolved); add new §6.4 divergences §6.5.1: topic-naming inconsistency is now resolved — all four .list surfaces use ovos.<domain>.<verb>. Update the table and replace the 'not yet uniform' note with a rename log. §6.4: add four new divergence entries: - Skill self-identification on every emission (INTENT-4 §3.1) - recognizer_loop:utterance de-prescribed (PIPELINE-1 §9.1) - .list topics standardized - (keeps the existing scope-discriminator / handler-trio / per-pipeline_id / utterance.handled entries) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: cleanup — drop draft-history meta-commentary Stand-alone design notes, not a changelog. §4 design rationale: rewrite Session block and TRANSFORM-1 lang bullet to describe current design, not 'moved from earlier draft'. §6.4 divergences: rewrite handler-trio / trio-ownership / scope- discriminator / skill_id-emission / recognizer_loop / topic-naming entries to state current design, not contrast with earlier drafts. §6.5.1 introspection patterns: drop 'in this round' rename note. §9 (rewritten 'Design history' → 'The spec set, in three stacks'): drop §9.3 audit-driven-refinement entirely (changelog content); merge §9.1 + §9.2 into one tighter section about how the eight specs partition and what reference implementations exist. §10 compatibility levels: soften 'was previously spoken of at' to 'is spoken of at'; replace the 'no longer describes' framing with a forward-looking 'tuple covering all eight specs is a planned follow-up'. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: update divergence catalog for CONTEXT-1 key-shape collapse + dispatch stamping §6.4: rewrite the CONTEXT-1 scope-discriminator entry to reflect the bigger change — scope AND origin both collapsed into the key shape. requires_context discriminator is the surviving surface (default private). §6.4: rewrite the skill_id-on-every-emission entry to lead with the structural enforcement (dispatch stamping + forward/reply inheritance), with loader interception as a follow-up rather than the primary path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: clarify topic-naming claim as prefix-uniform, verb depth varies * APPENDIX §6.5.1: flag the 'intent' word collision across three introspection topics Cross-spec audit B1: 'intent' plays three different roles across the four-spec introspection table — registered intents (INTENT-4), compiled-in-a-matcher intents (PIPELINE-1), and intent-transformer plugins (TRANSFORM-1). The shapes are deliberate and the payloads are distinct, but the topic strings read confusingly at a glance. Added an informative paragraph naming the three meanings and clarifying that ovos.transformer.intent.list follows the per-chain ovos.transformer.<type>.list pattern, where 'intent' is the chain type — not a listing of intents. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §4 Transformer: design note on the six per-type self-identification keys Document the rationale for TRANSFORM-1 §1.3 claiming six per-type context keys (audio_transformer_id, utterance_transformer_id, ...) rather than a single generic transformer_id. Two arguments: (1) role preservation across the six-stage chain, mirroring the per-type partition that already exists in §1.1 registries, §5 session overrides, and §6 introspection topics; (2) multi-type- plugin disambiguation, since §1.1 permits a single transformer_id across types and a generic context key would erase the role at emit time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §4 Transformer: record list-valued attribution, denylist symmetry, and the per-type field-count tradeoff Four design notes capturing the recent TRANSFORM-1 evolution: - Update the existing per-type self-id bullet to reflect the plural list-valued context keys (audio_transformer_ids etc., not the older singular names). - New bullet: list-valued attribution preserves full chain provenance per type; the last entry is the most-recent stamp. Skills and pipelines stay single-string because they originate rather than chain. - New bullet: per-type denylists (six blacklisted_*_transformers) complete the policy surface, mirroring PIPELINE-1's pipeline/blacklisted_pipelines pair. Three-stage composition (preference → availability → policy) parallels PIPELINE-1 §5.5. - New bullet: acknowledge the per-type 'explosion' (12 session fields + 6 context keys), defend the choice against the transformer_<type>:<name> prefix-encoding alternative (direct lookup vs prefix parsing), note that SHOULD-omit makes the common case zero-cost on the wire, and document the object-valued form as a clean fallback if the field count ever proves painful in practice. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §4 CONTEXT-1: rationale for default-private scope Add design-rationale paragraph explaining why ovos.context.set defaults to private scope when the canonical worked example (Person → Bob) is naturally cross-skill. Three reasons: migration fidelity (current Adapt set_context is effectively skill-private), safer footgun direction (accidental shared-leak is harder to debug than accidental cross-skill miss), and authorability (cross-skill coordination deserves a conscious explicit scope). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §6: record recognizer_loop:utterance -> ovos.utterance.handle rename Move the entry-topic from §6.1 'already aligned' to §6.4 'architectural divergences' — it is no longer a name kept verbatim, since PIPELINE-1 §9.1 now prescribes ovos.utterance.handle. Rationale paragraph cites the three MSG-1 §2.1.2 naming convention violations: ':' as separator, implementation-role leading segment, missing request/terminal verb pairing. Migration cost spelled out (every audio-input service emits, every intent-service handler subscribes: ovos-dinkum-listener, ovos-simple-listener, ovos-audio, ovos-core/intent_services). §6.7 predecessor-topic table updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: §2.5 Rasa/hassil/ASK/Mycroft comparisons; §6.5.2 session-field + stamp-rule cheat-sheets Two informative additions: - §2.5 (new): extends the §2 comparison set with Rasa, hassil, ASK / Dialogflow, and Mycroft. Locates the CONTEXT-1 design against Rasa's policy-engine-coupled forms; locates TRANSFORM-1 §3.4 against ASK/Dialogflow built-in entity types as the injectable open contract; documents Mycroft as the predecessor whose ad-hoc model the spec family formalizes. - §6.5.2 (new): session-field cheat-sheet consolidating the 26 fields claimed across SESSION-1, PIPELINE-1, TRANSFORM-1, and CONTEXT-1 into a single reference table — owner spec, role (preference / policy / signal / identity), empty-array semantics. Followed by a stamp-rule cheat-sheet covering the three component-identity context-key surfaces (skill_id, pipeline_id, <type>_transformer_ids) and their behaviour across origination, .reply / .response, and .forward. Both reduce cross-spec bouncing for implementers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: reorganize from 10 sections to 7, restructure for flow The appendix had become a dumping ground after multiple rounds of additions. Restructured with clear narrative flow: §1 About the OVOS specifications — formalization framing, the three-stack overview (was §9), compatibility levels (was §10), reference implementations + ecosystem tooling (folds in ovos-spec-tools from §9 and ovos-localize from §8). §2 Comparison with other voice-assistant systems — merges the HA/Rhasspy material (was §2) with the Rasa/ASK/ Dialogflow/Mycroft/hassil material (was §2.5) into a single comparator section, ordered by relevance: HA & Rhasspy (shared lineage) → open-vs-closed structural argument → Mycroft (predecessor) → Rasa (CONTEXT-1 comparator) → ASK/Dialogflow → hassil (grammar-only) → summary of where OVOS leads/follows/differs. §3 Architectural patterns — the bus as substrate (was §5) and the pipeline-plugin model (was §3) grouped as the two cross-cutting architectural moves. Bus-substrate section gains an explicit subsection on the layer-2 authorization story (preference / policy split). §4 Design rationale, per specification — was §4 itself but now systematically per-spec (INTENT-1+2+3 grouped, MSG-1, SESSION-1, INTENT-4, PIPELINE-1, CONTEXT-1, TRANSFORM-1). Stale references purged; recently added rationales (most-specific-wins precedence, bidirectional lang propagation, per-type denylists, etc.) folded in. §5 Where the specs differ from current OVOS code — was §6 but reorganized: removed the §6.5.1 introspection- patterns table and §6.5.2 cheat-sheets (they aren't divergences from code, they're implementer reference — moved to §6). Renumbered to §5.1–§5.7. §6 Implementer reference — new top-level section gathering the cross-spec reference tables that were scattered: topic-name conventions (with the 'intent' overload clarification), session-field cheat-sheet, component-identity stamp-rule cheat-sheet, introspection patterns table. These don't belong inside a 'divergences from code' section; they're how-to material for fresh implementers. §7 Known gaps and planned work — unchanged content, last section. Trimmed stale entries about CONTEXT-1 and TRANSFORM-1 as 'planned' (they've shipped); added conversation-level evaluation infrastructure as a gap. Net: same content, far more navigable. Cross-references updated throughout. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §2: drop Mycroft comparator subsection; renumber 2.4-2.7 to 2.3-2.6 Mycroft AI Inc shut down in 2023; the fork is years old and the intervening design is not Mycroft's. Keeping a 'comparison to predecessor' subsection over-attributes the architecture and mis-frames OVOS as a derivative project rather than a long- running open project in its own right. Section §2 is now a comparison with currently-relevant voice-assistant systems only: - §2.1 Home Assistant and Rhasspy (shared grammar lineage) - §2.2 Closed domain vs open ecosystem - §2.3 Rasa - §2.4 Amazon ASK / Google Dialogflow - §2.5 hassil - §2.6 Summary Collateral: dropped Mycroft from the project-name list in the intro and from the comparator enumeration in the §2.6 summary. Legacy topic strings that happen to contain 'mycroft' in their literal name remain in the §5 divergence tables and §5.7 predecessor-topic mapping as factual code references. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §3.3: external-protocol interoperability injection points Make the family's interop story explicit rather than implied. New §3.3 catalogues three injection points where external protocols plug into the spec family: 1. Pipeline plugins as the dispatch-layer adapter — LLM APIs (OpenAI Chat Completions and compatible), deterministic template matchers (hassil), external intent classifiers, agent-tool protocols (MCP). 2. Transformer chains as the artifact-pipeline adapter — bidirectional translation, STT validators, content-policy filters, acoustic-event detectors. 3. Bus boundary as the wire-level adapter — Wyoming bridges, MQTT-based stacks, HiveMind-style layer-2 substrates. Per-protocol notes for Wyoming, OpenAI, MCP, hassil, MQTT, A2A — naming where each plugs in. The single-flip routing and no-central-state stance (§3.1) are what make the bus-boundary adapter feasible without modifying the assistant core. Concrete suggestion: a translation tool between OVOS-INTENT-2 locale resources and HA's hassil/intents YAML would let the two corpora cross-pollinate mechanically. Added to §7 known gaps as planned tooling. The three injection points are intentionally not exhaustive — they're the points the spec family deliberately keeps clean. A protocol needing deeper integration is a signal of architectural overlap rather than complementarity. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX: add CONVERSE-1 to orchestrator-stack narrative; close multi-turn gap OVOS-CONVERSE-1 (PR #25) fills the multi-turn conversation gap that §7 previously listed as planned work. Update §1.2 stack description to include it, and drop the §7 gap entry. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §5.3, §5.4: update for PIPELINE-1 §4.2 relaxation + §7.0 polymorphism collapse Two divergence-catalogue entries updated to reflect the PIPELINE-1 restructure: - The §5.4 'side-effect-free during match' entry is rewritten as 'match contract is the single obligation' — match's only MUST is returning Match-or-null; bus emissions during match are allowed; session mutation during match is via Match.updated_session (explicit channel). - New §5.4 entry: 'Match.updated_session as the match-phase session channel' — promotes the existing ovos-core code pattern `sess = match.updated_session or SessionManager.get(message)` to a normative Match field. Claiming plugin's mutations land; declined plugin's mutations drop at the boundary. - The §5.3 'Dispatch payload uses polymorphic owner_id' entry is rewritten as 'unified owner_id' — reflects PIPELINE-1 §7.0's collapse to two handler-owner shapes (plain skill, pipeline plugin with bundled handlers where pipeline_id == skill_id) plus the pure-matcher recognition. Notes the conceptual mapping skill_id ≈ voice_app_id, pipeline_id ≈ matching-engine id. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §1.2, §7: SESSION-2 fills the lifecycle gap OVOS-SESSION-2 (in flight at PR #27) defines session lifecycle and state ownership. Update: - §1.2 orchestrator-stack narrative adds SESSION-2 to the stack description with one-line summary of its scope (stateless orchestrator for named sessions, orchestrator-owned default session, projection mandate). - §7 gap entry rewritten: SESSION-2 lands the lifecycle piece; what remains deferred is the set of session preference fields that need to be claimed under SESSION-1 §2.1 by their owning specs (preferences / OCP / persona / locale). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §1.2: SESSION-2 narrative — SHOULD-project + MAY-internal (not 'mandate') Sync with SESSION-2 §2.4 relaxation (commit 6a882c8). The projection pathway is SHOULD-when-practical; plugins MAY hold internal state with full lifecycle ownership and best-effort resumption. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * APPENDIX §5.2.1: document ovos.session.sync / update_default for removal These ovos-core topics are not defined by any spec. SESSION-2 §6.4 explicitly avoids naming them. They should be retired in favour of clients reading session state from normal Message flow (ovos.utterance.handled or any other session-carrying Message). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * README + APPENDIX §1.0: establish voice OS framing README intro replaced: "voice assistant ecosystem" → "voice operating system" with an OS-analogy table (scheduler, IPC, shared memory, process supervision, loadable modules, syscall ABI). APPENDIX §1.0 (new): The voice operating system concept — two conflations addressed: (1) voice assistant product (closed, vertically integrated vs open platform); (2) LLM wrapper (LLMs fit as pipeline plugins, utterance/dialog/metadata transformers — one possible multi-role deployment, not the architecture itself). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * revert: move README voice-OS framing to its own PR (#28) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * APPENDIX: fix stale PIPELINE-1 refs; slim redundant prose - owner_id → skill_id throughout (§3.2, §3.3, §4.5, §4.6) - match(utterance,…) → match(utterances,…) (§4.5) - Match.captures → Match.slots (§4.7) - complete_intent_failure → ovos.intent.unmatched in §5.1/§5.3/§5.7; add rename row to §5.2 table - Dispatch payload block in §5.3 rewritten: {lang, utterance, slots}, handler-lifecycle uses {skill_id, intent_name, optional exception} - §5.5: add ovos.intent.unmatched and ovos.utterance.speak entries - §2.5 hassil: drop standalone subsection; fix §2 intro cross-ref - §1.3 compat levels: condense to bullets - §1.4: drop ovos-localize "honest notes" paragraph - §3.1.3: trim to essential bus-substrate mechanics - §4.7: trim per-type-explosion and per-type-self-id bullets - §5.4: trim rename and match-contract entries Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: OVOS-INTENT-4 — intent and entity registration and dispatch bus contract
Defines the full bus lifecycle of an intent on top of OVOS-MSG-1 and
OVOS-INTENT-3: registration, match notification, dispatch, and the
handler-lifecycle trio.
Topics defined:
- ovos.intent.register.keyword / .template — atomic per-intent
registration (replaces register_vocab + register_intent /
padatious:register_intent dance)
- ovos.entity.register — entity-hint registration (replaces
padatious:register_entity)
- ovos.intent.deregister / .enable / .disable, ovos.entity.deregister,
ovos.skill.deregister
- ovos.intent.list / .describe — introspection
- ovos.intent.matched — broadcast match-result notification
- <skill_id>:<intent_name> — dispatch topic, kept verbatim from
current OVOS so existing skill handler subscriptions need no change
- ovos.intent.handler.{start,complete,error} — handler-lifecycle
trio (renamed from mycroft.skill.handler.* for uniformity)
Architectural model: the host is the sole bus consumer of skill-
originated registration topics and delegates matching to its engines
through a host-internal interface. Engines do not subscribe to the
bus topics defined here. Aligned to OVOS-INTENT-3's keyword/template
duality.
The handler reference is local to the skill and never crosses the
bus; only the dispatch Message and the handler-lifecycle messages do.
APPENDIX additions:
- New §4 subsection: INTENT-4 design rationale (host as sole consumer,
atomic registration, kept dispatch topic, trio not response, rename)
- §6 (Where specs differ from OVOS) extended with INTENT-4-specific
subsections: topic renames, payload shape changes, architectural
divergences, things deliberately not changed
- §9.1 design history updated to mention five specs in two stacks
This is the spec that needs more review and more implementation effort
than MSG-1 — it changes registration topic names and payload shapes,
introduces the host-mediated architectural model (today some engines
subscribe directly to bus topics), and prescribes a uniform `captures`
map for dispatch payloads.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: INTENT-4 — distinguish utterance-layer from handler-layer lifecycle
The current §12 trio (handler-layer) and §10 match notification were
read as the only lifecycle signals around an utterance. They aren't.
ovos-core's pipeline emits utterance-layer messages that operate at
a different level and must not be confused with handler signals.
Verified flow in ovos-core/intent_services/service.py:
recognizer_loop:utterance → entry
├─ transformer cancel: ovos.utterance.cancelled + ovos.utterance.handled
├─ pipeline match: <skill_id>:<intent_name> → handler trio
│ + ovos.utterance.handled (from workshop _on_event_end)
└─ no match: complete_intent_failure + ovos.utterance.handled
ovos.utterance.handled is the universal end-marker, fired on every
terminal path; complete_intent_failure is the intent-layer failure;
the handler trio is purely handler-layer. The previous Appendix A
entry calling ovos.utterance.handled "subsumed by
ovos.intent.handler.complete" was wrong — they're at different
lifecycle levels.
Changes:
INTENT-4 §1 scope: explicitly defers the broader utterance lifecycle
and the intent-layer failure signal to the future pipeline spec.
INTENT-4 §12.6 (new): "This trio is handler-layer only" — table of
the three lifecycle levels (utterance / intent matching / handler)
and how implementers should distinguish them. Points forward to §15.
INTENT-4 §15 (new): "Other utterance-lifecycle messages (out of
scope)" with four subsections:
- §15.1 ovos.utterance.handled is utterance-layer, not handler-layer
- §15.2 complete_intent_failure and handler error are different
- §15.3 The deferred trio — table of the three trios and what
this spec formalizes vs defers
- §15.4 A known asymmetry in current OVOS: workshop's
_on_event_error doesn't emit ovos.utterance.handled. The spec
declares the invariant normative; the workshop fix is a
separate implementation bug.
INTENT-4 Appendix A: fix the wrong "subsumed" claim on
ovos.utterance.handled (mark as unchanged with §15.1 explanation);
add three new rows for complete_intent_failure,
ovos.utterance.cancelled, recognizer_loop:utterance — all marked
unchanged-and-out-of-scope, pointing at §15.
APPENDIX §6.4: add the workshop _on_event_error asymmetry as a known
architectural divergence with a forward reference to INTENT-4 §15.4.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs: OVOS-INTENT-4 — pure registration-protocol spec; appendix moved out
Refactors INTENT-4 around the pipeline-plugin model from
OVOS-PIPELINE-1:
- Registrations are **broadcast** on the bus. There is no central
party that owns, validates, or routes them. Pipeline plugins
(OVOS-PIPELINE-1) consume what they want. The orchestrator
passively indexes everything for the introspection topics.
The bus-level utterance lifecycle (match-result notification,
dispatch, handler-lifecycle trio) moved out to OVOS-PIPELINE-1
where it belongs.
Sections removed (moved to OVOS-PIPELINE-1):
- The match-result message (ovos.intent.matched)
- Dispatch (<skill_id>:<intent_name>)
- Handler-lifecycle messages (ovos.intent.handler.*)
- Other utterance-lifecycle messages
Sections rewritten:
- §1 Scope — shorter; registration topics only; explicit pointer
to PIPELINE-1 for the rest.
- §2 Architectural model — "registrations are broadcast" (no
central consumer or router). Orchestrator is passive indexer.
- §3.2 Responses — plugin-optional; no orchestrator handshake.
- §3.3 Error codes — plugin-emitted only; drops
`no_compatible_engine` (no central rejection party).
- §4 Topics table — broadcast direction marked explicitly.
- §10 Introspection — reframed as orchestrator's passive
registration index.
- §11 Conformance — split per-party (skill MUST, plugin MAY,
orchestrator MUST).
Also folds in:
- Rename host → orchestrator (companion to INTENT-3 v1.1 in #13).
- Removal of the in-spec legacy-mapping appendix (the spec body
is now timeless; predecessor-topic mapping is implementation
history and belongs in APPENDIX.md, covered in a separate
commit).
The spec body now references no specific code paths, no Mycroft
history, no "current OVOS" qualifiers. All such context lives in
APPENDIX.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: release as v2 (incompatible with current OVOS)
Per the draft-stage versioning policy, v1 is reserved for content
drop-in compatible with current OVOS. INTENT-4's topic-namespace
rename and the orchestrator passive registration index both
require OVOS-side changes, so the first release ships as v2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 CHANGELOG: clarify V2 trigger is the topic rename, not the passive registry
* GLOSSARY: fix stale session.pipeline_stages → session.pipeline
* scope: drop APPENDIX/CHANGELOG/GLOSSARY/README/intent-definition; PR #9 is intent-registration.md only per 1-file rule
* OVOS-INTENT-4: announce-on-load + pull-query pattern; skills are authoritative responders
Two related changes around §10 introspection:
§2 — orchestrator's passive index is now MAY, not MUST. Skills
are the authoritative source for their own registrations. The
orchestrator is a logical role; it MAY be implemented as multiple
cooperating processes (PIPELINE-1 §2), each maintaining its own
index slice and responding independently to §10 queries.
§10 — reframed as **broadcast-query / scatter-response** to
explicitly handle load-ordering:
- Registration topics (§5-§8) are themselves load-time
announcements. Consumers that subscribed before the skill loaded
receive them in real time; consumers that started later have
missed them.
- Pull-query via §10 topics is the catch-up mechanism. Skills
MUST respond to queries that match their own registrations
(authoritative source). Orchestrator MAY also respond from its
passive index as a convenience. Consumers aggregate responses
across responders.
- The bus is async with no completeness signal; consumers wanting
guaranteed completeness keep their own roster of expected
responders and time out non-responders.
§11 conformance updated:
- Skills MUST respond to §10 queries for their own registrations.
- Orchestrator's index becomes MAY (was MUST). When maintained, the
prior MUST-bullets apply. An orchestrator without an index is
conformant — skills handle the introspection load.
This aligns INTENT-4 with the same pattern TRANSFORM-1 §6 uses:
producers MAY announce on load (these are the §5-§8 broadcasts);
consumers MUST query when they need accurate state because load
ordering between producer and consumer on the bus is not
guaranteed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: orchestrator owns the manifest; skills have no .list duty
§10 (rewritten): the introspection surface is the orchestrator-
owned manifest of observed registrations. Skills have NO
introspection obligation under this spec — they emit
registrations once at load and move on. Per-pipeline-plugin
compiled state is queried via OVOS-PIPELINE-1 §10 (distinct
surface). Adds pull-query-is-source-of-truth boilerplate
parallel to TRANSFORM-1 §6 / CONTEXT-1 §5.4.
§3.3: add idempotent-deregistration carve-out. Plugins SHOULD
reject unknown_intent / unknown_entity / unknown_skill but MAY
treat as no-op success during shutdown. Producers wanting
idempotent removal MAY ignore unknown_* codes.
§11 conformance: drop skill MUST-respond to §10 queries (no
skill-side obligation). Orchestrator promoted from MAY-index to
MUST-maintain-manifest + MUST-serve-§10-queries. Split-
orchestrator: per-process manifest slices, consumers aggregate.
See-also: add MSG-1 §2.1.1 reference, SESSION-1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: skill_id emission discipline; RFC 2119 tightening; split-orch ref
§3.1 (new): A skill MUST set Message.context["skill_id"] on every
Message it emits. This is the authoritative attribution surface
for skill-originated bus traffic; observers MUST NOT infer the
originating skill from data fields or topic names. Specifically
a skill discipline — orchestrator and infrastructure components
identify via source and component-specific context keys.
§3 renumbered: §3.1 Identity → §3.2; §3.2 Responses → §3.3; §3.3
error_codes → §3.4. All internal cross-refs updated.
§2: tighten with explicit MUST NOT on producer-side acknowledgement
reliance. Reduce split-orchestrator restatement to a one-sentence
reference to PIPELINE-1 §2. Drop the "skill-side authoritative
record" language that no longer applies after the §10 rewrite
(orchestrator owns the manifest now; skills have no .list duty).
§11 skill conformance: add skill_id-on-every-emission line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: fix source-as-identity wording — source is opaque metadata
Per design correction: no spec prescribes source as an identity
surface for any component. It is opaque metadata typically populated
by the originator and propagated by MSG-1 §5 derivations.
Components that need to identify themselves use their own
component-specific reserved context keys (pipeline_id,
transformer_id, future entry-service identity).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: clarify data.skill_id vs context.skill_id + loader enforcement
data.skill_id (when defined by a topic's payload schema) is the
SUBJECT of the operation — a search filter, a target, an owning
skill. context.skill_id is the EMITTER. The two MAY differ.
Adds orchestrator-side enforcement rule: a skill loader MUST
intercept the skill's emit pathway and enforce
context.skill_id == loaded skill's skill_id whenever possible, so
non-compliant handlers can't escape attribution. Places the
discipline on infrastructure, not skill authors.
Cross-references CONTEXT-1 §5.2 as an example of a topic owning
spec that rejects messages lacking context.skill_id.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.3: producers MUST NOT block waiting for .response; use §10 instead
Closes the response-timing gap: producers naively blocking on
.response could wait indefinitely (the spec already says emission
is plugin-discretionary). Explicit MUST NOT block, plus pointer to
§10 manifest query as the supported verification path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: structural enforcement via dispatch context.skill_id stamping
Per design clarification: the cleanest enforcement path is
structural, not interception-based. The orchestrator stamps
context.skill_id from the dispatch topic prefix
(PIPELINE-1 §7.1); skill emissions via forward/reply inherit
automatically. Interception is reserved for emissions outside
the dispatch path (background workers, initiative-driven traffic).
A drift between context.skill_id and the dispatch <skill_id> is
malformed and detectable — the spec now names this as the
authoritative non-conformance signal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §4: fix PIPELINE-1 section pointer §§9–10 → §§7–9
Cross-spec audit: the trailing pointer in §4's topic table cited
'OVOS-PIPELINE-1 §§9–10' for match notification, dispatch, and
the handler-lifecycle trio. PIPELINE-1 actually houses these in
§§7–9 (§7 dispatch, §8 handler-lifecycle, §9 utterance-layer
events). Old reference pre-dated a PIPELINE-1 numbering revision.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: split stamp rule (originate + modify); add coexistence-with-other-identity-keys paragraph; update Other component types cross-ref
Aligning skills' rule with the uniform MUST-stamp-on-originate-or-
modify discipline adopted across PIPELINE-1 §3.1 and TRANSFORM-1
§1.3.
Added explicit coexistence paragraph: a Message MAY carry other
component-identity keys (context['pipeline_id'], the six
<type>_transformer_id keys) in addition to context['skill_id'].
MSG-1 §5 derivations preserve all such keys; this spec doesn't
require skills to strip them, and consumers MUST NOT treat
multiple identity keys as malformed. Attribution resolves via
CONTEXT-1 §5.2 precedence.
Other component types subsection updated to name the parallel
disciplines (PIPELINE-1 §3.1, TRANSFORM-1 §1.3) and to list the
six per-type transformer keys explicitly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: stamp rule covers derivations placed on the bus by the skill
Same correction as PIPELINE-1 §3.1: a derivation (forward, reply,
response) the skill performs and emits IS a skill emission. The
resulting Message-on-wire MUST carry context['skill_id'] regardless
of derivation mechanism. The most common case — a handler's .speak
forwarded from a dispatch — is already structurally satisfied
because the dispatch-stamping rule propagates the skill's own id
through .forward; the explicit rule binds for derivations from
non-dispatch sources.
Also updated cross-spec refs to TRANSFORM-1 plural list-valued
<type>_transformer_ids shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1, §3.3: forward exemption + tighten .response MUST/SHOULD
- §3.1 stamp rule: pure-forward propagation exempt. Same shape
as the PIPELINE-1 §3.1 / TRANSFORM-1 §1.3 changes: .reply /
.response are authorial and MUST stamp; .forward is propagation
and MUST NOT overwrite inherited context['skill_id']. The
dispatch-stamping rule keeps the typical .forward-from-dispatch
path correct by construction (the inherited value is already
the skill's own id).
- §3.3 response asymmetry: a consuming plugin now MUST emit
ok: false on rejection (silent rejection is non-conformant —
the producer cannot distinguish 'rejected here' from 'no
consumer'), and SHOULD emit ok: true on success. The
asymmetry is justified: rejection carries actionable
information; success is more reliably observed via §10
introspection than per-emission ack.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §8.4: relax MUST-reject to SHOULD; align with §3.4 idempotent-deregistration carve-out
§3.4 already permitted plugins to respond {ok: true} on
idempotent deregistration (a deliberate carve-out for skill
shutdown sequences). §8.4 still demanded MUST-reject with
unknown_*. The two contradicted.
Aligned: §8.4 is now SHOULD-reject, MAY-no-op-success, with
cross-reference to §3.4's carve-out. Both responses are equally
conformant; choice is per-plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.4, §3.5: reject registrations of reserved intent_names
Add new error_code 'reserved_name' and a §3.5 cross-reference to
OVOS-PIPELINE-1 §7.3's reserved-intent-name registry. The orchestrator
MUST reject registration payloads that name an intent_name reserved
by a companion specification.
Currently reserved: converse and response, both by OVOS-CONVERSE-1
(PR #25). The reservation registry is owned by PIPELINE-1; this
spec is the enforcement point at registration time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: drop authorial-action framing; cross-ref PIPELINE-1 §7.1
Replace "authorial action" / "modify-in-place" / .reply/.response
authorship prose with a concise rule. The dispatch-stamping
enforcement detail re-specified here now simply points to
PIPELINE-1 §7.1, which owns it.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* INTENT-4: fix stale topic name, terminology, and grammar
- §1 non-goals: complete_intent_failure → ovos.intent.unmatched
- §5.1: "captures map" → Match.slots (consistent with PIPELINE-1)
- §7.1 / §7 field ref: INTENT-2 §4.4 → §4.3 (correct section)
- §5.3, §6.2, §6.3, §7.2, §8.4: "A orchestrator" → "An orchestrator"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* INTENT-4: fix four merge blockers
- §3.5 vs §11 contradiction resolved: reserved_name rejection is now
a plugin obligation, not the orchestrator's — the orchestrator is
a passive manifest listener and MUST NOT synthesize responses
(§11 is now internally consistent)
- §8.5: "retains the definition and handler binding" → "retains the
definition in the manifest" — handler bindings never cross the bus
(§9), the orchestrator has no knowledge of them
- §8.4: "a orchestrator" → "an orchestrator" (lowercase missed by
earlier replace_all)
- §10.2: "file forms expanded to inline where possible" → normative
rule: describe response is always inline form; if file-only,
orchestrator MUST omit definition and include a warning
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* INTENT-4 §3.2 / §8.1: fix two stale cross-refs; tighten §8.1 reset rule
- §3.2 lang field cited OVOS-MSG-1 §4.2 (layer-2 picture) for the
data.lang vs session.lang distinction; correct target is
OVOS-SESSION-1 §3.2 (Language signals). Also pin lang
case-insensitivity per SESSION-1 §3.2.
- §3.2 closing line referenced §7 for replacement semantics; §7 is
entity registration. Correct target is §8.1.
- §8.1 said replacement preserves enabled/disabled state "unless the
producer explicitly resets it" — but §8.5 phrased the same carve-out
as "unless the producer deregisters first." Align on "deregister
first," the bus-observable operation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: pre-merge polish — trim §3.1, pin empty-expansion, Postel keys, multi-plugin .response, drop §10.2 warning string
- §3.1: collapse the orchestrator-enforcement and other-component-
types sub-sections from ~3 pages to one combined paragraph. The
normative content (loader-side stamping, dispatch-path structural
conformance, symmetry with pipeline_id / transformer_ids) is
retained verbatim; the per-key enumeration of TRANSFORM-1's six
ids is dropped — readers consult TRANSFORM-1 §1.3 directly.
- §5.3 (keyword constraint validity): add the missing
empty-expansion check — a vocabulary descriptor whose samples
expand to zero non-empty entries (INTENT-1 §3.6) is malformed.
Also pin consumer-side Postel rule: a producer MUST include all
four keys, but a consumer SHOULD treat a missing key as an empty
array rather than rejecting.
- §6.3 (template malformed payloads): same INTENT-1 §3.6 carve-out
as keyword — a template that expands to zero non-empty samples
is malformed.
- §3.3 (responses): define multi-plugin consumption semantics —
multiple plugins MAY each emit independent .response Messages;
any ok: false is authoritative for that plugin's rejection only;
no cross-plugin aggregation rule; manifest of §10 is the single
answer to "did anyone consume this?"
- §10.2 (describe response): drop the stringly-typed `warning`
field for unresolvable file: registrations. Absence of
`definition` on an `ok: true` response now carries that signal
on its own — no enum churn, no free-form prose on the wire.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: drop .response acknowledgement and data["skill_id"] noise
Two simplifications:
1. Registrations become fire-and-forget. The .response reply
mechanism (§3.3), the error_code enum (§3.4), and every
"MUST reject with error_code: …" clause across §§5.3, 5.5,
6.2, 6.3, 7.2, 8.4, 8.5 are gone. A consuming plugin that
judges a registration malformed (or finds it names a reserved
intent_name) simply doesn't index it; the manifest of §10
reflects what landed. Producers verify via §10 query, never
by waiting on an ack. Deregistration becomes naturally
idempotent (no_op semantics) without the prior unknown_*
error-code carve-out.
2. The context["skill_id"] vs data["skill_id"] disambiguation
sub-section (~30 lines) is dropped, along with the §3.2
"data is authoritative" paragraph. Payloads still carry
skill_id as a payload field where the topic schema needs
it — that's just a field name, not a discourse that earns
half a section of normative prose.
3. Version bumped to 3.
Net effect: -153 lines, removes a class of edge cases
(multi-plugin conflicting .response, silent vs structured
rejection, error_code precedence) without losing any
observable behaviour — the manifest was already specified
as the single source of truth, the .response was always
optional, and the error_codes were always plugin-emitted.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 §3.1: merge "Coexistence with other identity keys" into the enforcement sub-section
The standalone paragraph repeated the pipeline_id / transformer_ids
enumeration that the "Enforcement and other component types"
sub-section already covered, and split the cross-component
discipline across two places. Merge: drop the standalone paragraph,
absorb its three normative bits (consumers MUST NOT treat extra
keys as malformed; skill MUST NOT strip keys it did not set;
CONTEXT-1 §5.2 precedence for single-owner attribution) into one
sentence at the end of the existing sub-section.
Also tighten the "authoritative attribution key" paragraph: drop
the parenthetical list of observer types (loggers, audit,
analytics, telemetry, debug tooling, manifest) — the abstract
"observers" carries the meaning without the enumeration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: aggressive simplification pass — drop duplicates and verbose restatements
Net: -122 lines (780 → 658), no normative change.
- §1: scope "does not define" list compacted from 7 bullets to 5;
redundant predecessor-topic-mapping bullet dropped (already in
APPENDIX), language-fallback bullet shortened, handler-reference
collapsed into the INTENT-3 bullet.
- §2: trimmed the "no plugin consumes it" worked example, dropped
the split-orchestrator paragraph (lives in §10), collapsed the
"two consequences" bullets, joined the manifest description into
one paragraph.
- §3.1: dropped the standalone "authoritative attribution key"
paragraph (overlapped with the rule above it) and the entire
"Consumer-side" sub-section (its content is implicit in
"context.skill_id is the only attribution key"). Merged the
must-not-reject diagnostic into the opening paragraph.
- §3.2 reserved-names: collapsed three paragraphs to one — the
authoritative-registry pointer was the only normative content.
lang cell tightened from a paragraph to one sentence.
- §4 trailing paragraph: 5 lines → 1.
- §5.5 file-form rule: tightened, renamed "single-orchestrator only"
to "single-host only" — describes the constraint, not a deployment
shape.
- §8.1 replacement: tightened reset clause.
- §9 handler reference: 3 paragraphs to 1.
- §10 introduction: collapsed 5 paragraphs (load-time announcements,
introspection surface, per-pipeline cross-ref, split orchestrator,
pull-query) into 3, keeping the pull-query-is-truth rule prominent.
- §10.2 describe response: tightened the file:-form omit-definition
rule, dropped the verbose "re-issue against an orchestrator that
shares filesystem" instruction.
- §11 conformance: dropped two redundant skill paragraphs (already
implied by §10 + manifest), dropped the trailing PIPELINE-1 §10
cross-ref paragraph (already in §10) and the orchestrator-
responsibilities filler.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 v4: address final-review findings — log floors, reserved flag, scope cleanup
Closes the design gaps from the pre-merge review.
Normative additions:
- §3.1: orchestrator MUST (was SHOULD) log absent or mismatched
context["skill_id"] at WARN. The "authoritative attribution key"
framing demanded strict enforcement.
- §5.3 / §6.3 / §7.2: the rejecting plugin MUST log malformed-payload
rejections at WARN with skill_id, intent_name/entity_name, lang,
and a one-line reason. Fire-and-forget removed the .response
channel; the WARN log is now the producer's only debugging signal.
- §3.2 + §10.1: reserved-name registrations are flagged with
reserved: true in ovos.intent.list responses. The split
enforcement (plugins don't index, manifest does) now surfaces the
misregistration through introspection instead of vanishing.
- §3.2 / §11: explicit "replacement is per-(skill_id, intent_name,
lang); other languages unaffected" — closes a cross-language
atomicity question.
- §8.4: pointer to PIPELINE-1 for races between deregistration and
in-flight match (utterance lifecycle owner's concern).
- §8.5: one-sentence rationale on why enable/disable is bus-level
rather than skill-side gating — external tooling (admin UI,
A/B experiments) suppression without skill-code changes.
- §5.5: producers SHOULD prefer inline unconditionally; file form
is portable only under shared filesystem.
- §10.2: consumers SHOULD fall back to PIPELINE-1 §10 per-plugin
introspection when ovos.intent.describe omits definition for an
unexpandable file: registration.
Scope cleanup:
- §3.1: cut the cross-component over-reach paragraph (pipeline_id,
transformer_ids enumeration). INTENT-4 should not prescribe
cross-component identity discipline; one-liner pointer to
CONTEXT-1 §5.2 for single-owner attribution is sufficient.
Removes two fragile forward refs (TRANSFORM-1 §1.3, the
PIPELINE-1 §3.1 enumeration).
- §5.3: dropped the producer-strict / consumer-lenient
contradiction. A payload missing required keys is malformed,
full stop.
§11 conformance updated to match: plugin MUST log rejections;
orchestrator MUST flag reserved entries and log skill_id drift.
Version bumped to 4.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 v5: drop file: form entirely — wire format is inline-only
A wire format spec has no business carrying producer-side filesystem
paths. Locale resource files (.voc, .intent, .entity, .blacklist —
OVOS-INTENT-2) are an authoring convenience; a skill loader reads
them and inlines their expanded content before emitting the
registration payload. File paths never appear on the bus.
Removed:
- §5.1 file-form vocabulary descriptor example and the samples-XOR-
file rule.
- §5.5 entire "File form is single-host only" section.
- §6.1 file-form template registration example, the `file` and
`blacklist_file` fields from the field table.
- §6.3 "both samples and file present" and "both blacklist and
blacklist_file present" malformed-payload bullets.
- §7.1 file-form entity registration example, the `file` field from
the field table.
- §7.2 "both or neither of samples/file" rule, replaced with the
simpler "samples is missing or empty" rule.
- §10.2 "registered via file: that we can't expand" omit-definition
carve-out — definition is always the broadcast payload as-is.
- §11 conformance §5.5 cross-ref and the §§5.5 entry in the plugin
malformed-registration list.
Added (§5.1): one-paragraph explanation that locale resource files
are a producer-side authoring convenience and never cross the bus.
Eliminates a class of silent failure (unresolvable file: paths
producing empty manifest entries with no diagnostic) and removes
the awkward "single-host only" hedge that admitted the feature was
broken for the deployment shapes the spec ostensibly targets.
Version bumped to 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update version number in intent-registration.md
* INTENT-4: allow dual-method registration — same intent under both keyword and template
Same handler, same intent identity, two training-data representations
on the wire. Different pipeline plugins consume different methods;
either match dispatches to the same <skill_id>:<intent_name> topic.
Whether to use one or both, and how to resolve a tie if both produce
a match, is pipeline policy (OVOS-PIPELINE-1).
Changes:
- §3.2: split the identity triple (skill_id, intent_name, lang) from
the registration key (skill_id, intent_name, lang, method). The
triple identifies an intent; the quadruple is what the manifest
indexes and what §8.1 replacement targets. A keyword and a template
registration of the same triple are independent manifest entries.
- §3.2: new paragraph stating dual-method registration is explicitly
permitted, what it means (two representations of the same handler),
and that conflict resolution is pipeline policy.
- §3.2: entity registrations clarified as having no method axis
(entities are a single shape).
- §8.2 deregister: clarified that the wire payload targets the triple
and removes ALL methods under it. No per-method deregistration —
ergonomic default. A skill wanting to keep one method
re-registers it.
- §8.5 enable/disable: same — target the triple, affect all methods.
- §10.1 list: explicit note that dual-method intents appear as two
entries distinguished by `method`.
- §10.2 describe: request gains an optional `method` filter; success
response reshaped to a `definitions: [{method, definition}, ...]`
array so both methods can be returned when no filter is given.
- §11 conformance: dropped the "never both for a single intent"
prohibition; replaced with explicit permission, cross-ref §3.2.
- §11 orchestrator: replacement-is-per-key clause updated to name the
quadruple key and note both other languages and the other-method
entry are unaffected.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4: final-review polishes + PIPELINE-1 §7.3 realignment
Three-file change. The PIPELINE-1 edit is included here (rather than
in its own one-file PR) because PIPELINE-1 is already merged on dev
and the §7.3 drift is a direct consequence of INTENT-4's
post-merge-prep simplifications — they need to land together to
avoid leaving dev with contradictory normative content.
INTENT-4 polishes (intent-registration.md):
- §3.2: dual-method registration paragraph clarifies that producers
MAY ship divergent suppression vocabularies per method (different
`excluded` for keyword vs different `blacklist` for template); each
plugin honours only its own method's suppression.
- §5.3: strengthened the WARN-log contract — MUST include the
rejecting topic alongside skill_id/intent_name/lang/reason because
the same triple may be valid as keyword and malformed as template
(or vice versa). Structured logging RECOMMENDED.
- §8.5: explicit footgun-mitigation note — to retain only one method,
deregister the triple (removes both) and re-register the desired one.
- §10.2: pin `definitions[]` ordering to `keyword`, `template` when
two entries are returned so consumers can rely on positional access.
- §10.2: include `reserved: true` at top level when the intent_name
is reserved (§3.2), so a `.describe` consumer can diagnose a
reserved-name misregistration without round-tripping through `.list`.
PIPELINE-1 fix (pipeline.md):
- §7.3 reserved-intent_names: realign with INTENT-4's actual handling.
Previously cited `OVOS-INTENT-4 §3.5` (the section was consolidated
into §3.2 during INTENT-4's pre-merge simplifications) and an
`error_code: "reserved_name"` mechanism (INTENT-4 dropped .response
entirely). Now correctly states: consuming pipeline plugin MUST NOT
index, MUST log per INTENT-4 §5.3; orchestrator manifest still
indexes the broadcast with `reserved: true`.
- Version bumped to 3.
CHANGELOG: added OVOS-PIPELINE-1 (v1/v2/v3) and OVOS-INTENT-4 (v1)
sections; PIPELINE-1's prior versions were merged without a changelog
entry, so the first two entries are housekeeping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* INTENT-4 / PIPELINE-1: reserved-name registrations are just malformed payloads
Collapses the entire reserved-name machinery into the existing
malformed-payload rules. The orchestrator no longer indexes reserved
registrations for diagnostic visibility, the `reserved: true` flag
in §10.1 / §10.2 is gone, and every consumer (orchestrator manifest
and pipeline plugins alike) treats a reserved intent_name as one
more bullet in the malformed-payload list — log at WARN, do not
index.
INTENT-4:
- §3.2: reserved-name paragraph reduced to one sentence pointing at
the §5.3 / §6.3 malformed-payload rules.
- §5.3 / §6.3: added "intent_name reserved by another spec" as the
first bullet of each malformed-payload list.
- §10.1: dropped `reserved: true` flag from list-response entries
and the descriptive paragraph; reserved registrations don't appear
in the manifest at all.
- §10.2: dropped the `reserved: true` field from describe responses.
- §11: orchestrator conformance no longer enumerates a
reserved-flagging duty; plugin conformance folds reserved-name
rejection into the malformed-registration cross-ref.
PIPELINE-1:
- §7.3: realigned the reserved-name rejection contract — registrations
naming a reserved intent_name are malformed; every consumer
(orchestrator + plugins) treats them under INTENT-4 §5.3. Removes
the stale `OVOS-INTENT-4 §3.5` reference and the prior
manifest-indexes-with-flag wording.
- No version bump (per project preference, drafts stay at their
current version until stable).
CHANGELOG: dropped the staged PIPELINE-1 v3 entry; the §7.3 fix
lands as a non-version-bumped draft polish.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d4eba6f to
5b64704
Compare
…TOP-1 - README: remove 'in review' PR links for CONVERSE-1 (#25) and STOP-1 (#33); both now merged; add STOP-1 to reading-order bullets - appendix/divergences: replace stale 'response-mode counter' reference with current session.response_mode field name Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Imperative complement to OVOS-CONTEXT-1's declarative gating. Formalises the three currently-informal continuous-dialog surfaces: - session.active_handlers: recency stack of recently-engaged owners, claimed under SESSION-1's field registry; auto-activated on dispatch, explicit activate/deactivate events, optional TTL prune. - The converse plugin role: a pipeline-plugin behavioural contract that offers each active owner the chance to claim the utterance before subsequent stages, via <owner_id>.converse.request/response with a reserved 'converse' match-result tag. - Interactive response collection: session.response_modes (single-holder invariant), enable/disable, <owner_id>.get_response delivery that bypasses every pipeline stage, timeout/retry/cancel wire shape. Defines evaluation order against CONTEXT-1 (response-mode pre-empts; converse claim before intent stages; gates apply to intent stages), extends PIPELINE-1's universal end-marker with converse- and response-mode-specific status values, and lists three orchestrator, plugin, handler, observer conformance roles. Single-file PR per repo policy. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… fix Bus surface cut from 14 topics to 7 (3 pairs + 1 delivery): - drop ovos.converse.activate / .deactivate — automatic dispatch activation (§3.1) plus direct session mutation (§3.2) cover every case without a third mechanism - collapse response_mode.enable / .disable into a single response_mode.set with mode: "response" | "intent" - drop ovos.converse.response_mode.retry — orchestrator-driven retry is incoherent on the wire (only the handler can re-prompt); handler-level retry loops are just two sets - drop the data.deactivate self-deactivate shortcut on converse.response — supplanted by direct mutation per §3.2 Structural rather than asserted invariants: - session.response_modes (map with single-holder rule) → session.response_mode (string | null). One slot = one string; the single-holder invariant is the shape, not a clause. - drop session.active_handler_ttl field; TTL becomes a deployer policy value by symmetry with the per-owner converse timeout and the size cap (both already deployer-tuned, not session- carried). Underspecification fixes: - §5.1 specifies what happens when the existing holder re-sets (resets the window) vs another owner (rejects with response_mode_held) - §5.3 adds optional auto_continue counter on set, addressing the race between get_response emission and handler re-set - §5.6 specifies session-expiry mid-wait: orchestrator emits get_response with status: "cancelled" before reaping Still v1 — version bumps happen on merge, not in-PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- §4.1: permit parallel poll, selection by highest activated_at (not
list position or arrival order); SHOULD skip blacklisted owners
- §4.2: poll response MUST use .reply derivation for remote-skill
routing, matching STOP-1 §4.2 rationale
- §4.5: clarify done-removal channel on null return — in-place session
mutation is the sole permitted exception to PIPELINE-1 §4.2
- §5.1: identity check promoted from SHOULD to MUST; unrecognised
owner_id in response_mode is a correctness hazard
- §5.1: orchestrator-clock note for expires_at evaluation; SHOULD
account for clock skew in distributed deployments
- §6.1: introspection response returns full {id, activated_at} objects,
not bare owner_id strings
- §9.3: .converse.request subscription demoted MUST → SHOULD; silence
is already defined as result:false/timeout, mandatory subscription
forces no-op boilerplate on non-converse skills
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Separate MUST and SHOULD handler obligations into distinct labelled lists; the prior edit left the opening "MUST:" with a SHOULD as its first bullet. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ponse :converse — handler volunteered via poll; still must parse the utterance :response — handler is captive recipient of a solicited answer; no NLU decision needed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…commitment - §4.2: poll MUST include NLU/parsing; result:true is a binding commitment; done signal belongs in the poll response - §4.3: semantic contract corrected — handler MUST handle fully on :converse dispatch, declining at dispatch time is non-conformant - §9.3: SHOULD bullet updated to reflect that poll is the decision point, not a pre-filter Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…TOP-1 - README: remove 'in review' PR links for CONVERSE-1 (#25) and STOP-1 (#33); both now merged; add STOP-1 to reading-order bullets - appendix/divergences: replace stale 'response-mode counter' reference with current session.response_mode field name Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ctive_handlers Rename the key in converse_handlers entries from `id` to `skill_id` for consistency with the active_handlers field shape (OVOS-PIPELINE-1 §7.1, updated by STOP-1 §6.2). Also register converse_handlers and response_mode in the ovos-session-1.md field registry, and update the stale active_handlers ownership reference from CONVERSE-1 to PIPELINE-1. Co-Authored-By: JarbasAI <jarbasai@users.noreply.github.com>
0d09ce2 to
bd2bcbf
Compare
…ponse_mode is load-bearing
…dler-side mutations
…ision with PIPELINE-1's active_handlers
…§4.3/§5/§7/§9.3; move invariants to §2.2
…en sentence, blank line
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@converse.md`:
- Around line 110-113: The spec currently treats session.converse_handlers as an
ordered list where index 0 is newest, which conflicts with the desired
timestamp-based recency; update the text for converse_handlers to state it is
unordered on-wire (an omitted/absent session.converse_handlers is equivalent to
[]), and require consumers to determine recency by sorting the entries by their
activated_at timestamp in descending order (newest first) when iterating or
resolving owners; reference the field names session.converse_handlers and the
activated_at timestamp in the description so implementers know to sort by
activated_at rather than rely on array index.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 002d685b-aed1-48dd-b6e6-912221420749
📒 Files selected for processing (4)
appendix/divergences.mdconverse.mdovos-session-1.mdovos-session-2.md
| The list is ordered **head-first by recency**: index `0` is the | ||
| most recently activated owner; index `n-1` is the least recently | ||
| activated of the surviving owners. An omitted or absent | ||
| `session.converse_handlers` is equivalent to `[]`. |
There was a problem hiding this comment.
Align converse_handlers wire semantics with timestamp-based recency (not list position).
This section makes on-wire order normative (index 0 == newest), which conflicts with the stated contract for this PR and creates interoperability risk when producers/transformers reorder arrays. Define the field as unordered on-wire and require consumers to sort by activated_at descending when evaluating recency/iteration.
Proposed spec text adjustment
-The list is ordered **head-first by recency**: index `0` is the
-most recently activated owner; index `n-1` is the least recently
-activated of the surviving owners. An omitted or absent
-`session.converse_handlers` is equivalent to `[]`.
+`session.converse_handlers` is an unordered on-wire set of entries.
+Consumers MUST determine recency by sorting entries on
+`activated_at` descending (most recent first) at read time.
+An omitted or absent `session.converse_handlers` is equivalent to `[]`.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@converse.md` around lines 110 - 113, The spec currently treats
session.converse_handlers as an ordered list where index 0 is newest, which
conflicts with the desired timestamp-based recency; update the text for
converse_handlers to state it is unordered on-wire (an omitted/absent
session.converse_handlers is equivalent to []), and require consumers to
determine recency by sorting the entries by their activated_at timestamp in
descending order (newest first) when iterating or resolving owners; reference
the field names session.converse_handlers and the activated_at timestamp in the
description so implementers know to sort by activated_at rather than rely on
array index.
When a handler enters response mode and emits ovos.utterance.speak as the prompt, it MUST set listen: true (PIPELINE-1 §9.6). The flag is a protocol-level statement that a follow-up utterance is expected; every output consumer re-opens the user input channel accordingly. Omitting it is non-conformant. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
owner_id was used throughout as the handler-identity concept. skill_id is the universal identifier per the architecture model. All 46 occurrences replaced globally; two tautological lines from the activation-update algorithm reworded to be concrete. Also: ovos.converse.active.list.response had session_id as a data field — removed; session identity is read from context.session.session_id. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@converse.md`:
- Around line 133-137: The docs define session.response_mode as containing
{skill_id, expires_at} while SESSION-1 defines the session registry entry as
{owner_id, expires_at}, causing a wire-shape mismatch; pick one canonical key
and update both places to match (either rename session.response_mode.skill_id →
owner_id everywhere or change SESSION-1.owner_id → skill_id everywhere), update
any references/comments that mention session.response_mode, SESSION-1, skill_id,
or owner_id to the chosen name, and ensure the prose clarifies that the chosen
identifier MUST NOT contain ':' and that expires_at accepts integer or float.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
| | Key | Type | Required | Meaning | | ||
| |-----|------|----------|---------| | ||
| | `skill_id` | string | yes | The handler that holds response mode for this session. MUST NOT contain `:` (MSG-1 §2.1.1 — colon-separated topic shapes use this identifier as a component). | | ||
| | `expires_at` | number | yes | Unix-seconds wall-clock time after which the wait window is stale. MAY be a float; consumers MUST accept integer and float forms. The plugin MUST discard a stale entry on the next match (§5.2) — the holder's framework-side timer drives the user-facing reaction. | | ||
|
|
There was a problem hiding this comment.
Cross-spec wire-shape mismatch for session.response_mode key name.
This file defines session.response_mode as {skill_id, expires_at}, but SESSION-1 currently lists {owner_id, expires_at}. That contract split can break implementers relying on SESSION-1 as the registry source. Please align both specs to one canonical key name in this PR.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@converse.md` around lines 133 - 137, The docs define session.response_mode as
containing {skill_id, expires_at} while SESSION-1 defines the session registry
entry as {owner_id, expires_at}, causing a wire-shape mismatch; pick one
canonical key and update both places to match (either rename
session.response_mode.skill_id → owner_id everywhere or change
SESSION-1.owner_id → skill_id everywhere), update any references/comments that
mention session.response_mode, SESSION-1, skill_id, or owner_id to the chosen
name, and ensure the prose clarifies that the chosen identifier MUST NOT contain
':' and that expires_at accepts integer or float.
Standardise on ping/pong for pipeline-plugin capability-discovery exchanges per AGENTS.md §4.5. The unicast dotted-addressed form <skill_id>.converse.ping / <skill_id>.converse.pong is consistent with ovos.stop.ping / ovos.stop.pong and the new fallback.ping / fallback.pong convention. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-1 conflict (preserve converse_handlers, apply skill_id compliance fix)
|
Merge-readiness: resolved. Conflicts resolved (commit aed3477):
Added FALLBACK-1, COMMON-QUERY-1, BRIDGE-1 in-review rows to README spec table. Semantic flag: The CI: GitHub shows UNSTABLE status but no workflow files exist in repo; CodeRabbit review has passed. UNSTABLE is likely a transient stale-check state. Note on #55: |
Companion issue: #24
Summary
Defines the imperative continuous-dialog surface: the converse plugin role that polls active handlers before intent matching, and the response-mode mechanism for collecting the next utterance exclusively.
What the spec covers
session.converse_handlers— recency-ordered list of{skill_id, activated_at}entries; deployer-tunable cap; TTL-pruned pre-pollsession.response_mode— single{skill_id, expires_at}entry when a handler holds the next utterance exclusivelyresponse_modefirst (pre-empts poll), then pollsconverse_handlersvia unicast ping/pong<skill_id>.converse.ping/<skill_id>.converse.pong(unicast dotted-addressed, sequential, stops at first claim). Handler-lifecycle trio NOT fired during poll{skill_id, expires_at}; entered/left via in-handler mutation; single-shot delivery via<skill_id>:responsedispatchactive_handlers; global stop empties bothKey design decisions
<skill_id>.converse.pingasks one skill at a time inactivated_atorder; stops at first claimerCompanion edits (same PR)
ovos-pipeline-1.md—active_handlersordering clarification; sort byactivated_atovos-stop-1.md—response_modefield reference fix