OVOS-SESSION-2: Session Lifecycle and State Ownership (draft)#27
Conversation
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughA new session lifecycle specification (OVOS-SESSION-2 v1) defines session state ownership, in-utterance mutation boundaries, per-utterance round semantics, client merge/adoption rules, resumption behavior, default-session persistence and restart semantics, and conformance requirements for bus, orchestrator, components, and clients. ChangesSession Lifecycle and State Ownership Specification
Estimated code review effort🎯 4 (Complex) | ⏱️ ~50 minutes Possibly related issues
Possibly related PRs
Poem
🚥 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 |
…sident response_mode; drop set topics Follow-up to OVOS-SESSION-2 (PR #27). The §2.4 projection mandate ("components MUST project session-keyed state into session- resident fields they own") requires CONVERSE-1's previously plugin-internal response-mode wait state to become a session- resident field. Three coordinated changes: 1. session.response_mode (§2.2) becomes a structured object {owner_id, expires_at, auto_continue}, replacing the prior single-string per-dispatch marker. The field is now persistent session state — it rides every Message that carries the session forward, lands in the client's local store (named sessions) or orchestrator's default-session store (default), and is visible to the plugin on every inbound utterance. 2. The side-band ovos.converse.response_mode.set / .set.response topic pair is removed entirely. A handler now enters response mode by mutating session.response_mode in-place within its dispatched handler (per SESSION-2 §2.6 handler-boundary mutation) and emitting any Message that carries the mutated session forward. The same in-handler mutation pattern handles leaving response mode (omit the field). Bus surface drops from 7 topics to 5. 3. §3.2 'Asynchronous mutation via bus events' is removed entirely (no bus events mutate session under the new model). §3 now lists three in-utterance mutation pathways: §3.1 automatic-activation-on-dispatch, §3.2 (renumbered) TTL prune, §3.3 (renumbered) transformer mutation. Plus the implicit §4.2 match-phase pathway (PIPELINE-1). Plus consequent edits throughout: - §1, §4 paragraph, §5 intro reframe response mode as session-resident, not bus-event-driven - §5.1 rewritten as in-handler-mutation pattern - §5.2 simplified: plugin reads session.response_mode directly, no plugin-internal state lookup - §5.3-§5.5 streamlined: timeout from expires_at, cancellation from in-handler removal, interrupt via framework - §9.1 orchestrator: drops set-event obligations; references SESSION-2 for session persistence - §9.2 converse plugin: holds NO cross-utterance state (§2.4 projection mandate); reads everything from session - §9.3 handler: enters/leaves response mode by session mutation, not bus events - §9.4 observer: can read session.response_mode for introspection - §10 non-goals: dedicated bus events for response mode are explicitly out - §1 build-on list: SESSION-2 added as dependency Five topics remain (down from 7): active.list pair, converse-poll pair, two dispatch topics for the reserved intent_names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
) 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>
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>
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 `@session-lifecycle.md`:
- Line 338: Choose one spelling variant for the word occurrences "recognises"
and "recognizes" (e.g., US "recognizes") and update every instance in the
document to that single variant; search for the literal strings "recognises" and
"recognizes", replace all matches with the chosen form, and run a quick
spellcheck to catch any remaining variants so the spec text is consistent.
- Around line 571-573: The cross-reference "§4.4" next to the emit description
for the universal end-marker "ovos.utterance.handled" (and PIPELINE-1 §9) points
to a non-existent section; locate the sentence containing
"ovos.utterance.handled" and replace "§4.4" with the correct target (the actual
section that defines client-side convergence, e.g., "§4.3" if that is the client
convergence section) or remove the numeric § reference and instead use a clear
textual reference ("the client-side convergence section") or an accurate link to
the intended section so the pointer resolves correctly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
Companion lifecycle spec to OVOS-SESSION-1 (which defines only
wire shape and explicitly defers lifecycle). Closes the
"session lifecycle" gap APPENDIX §7 has tracked since the spec
set's inception, and is the normative reference CONTEXT-1 and
CONVERSE-1 have been forward-referencing.
What the spec defines:
- §2 State-ownership model:
- 2.1 Bus is stateless transport
- 2.2 Orchestrator is stateless for named sessions
- 2.3 Orchestrator owns session_id == "default"
- 2.4 The projection mandate — components MUST project
cross-utterance session-keyed state into session-resident
fields they own (via Match.updated_session per PIPELINE-1
§4.2). Drives resumption parity (§5)
- 2.5 Clients own their named sessions; persistence is their
choice
- 2.6 In-place session mutation only at transformer, pipeline,
and handler boundaries — bus events do not mutate session
in the current utterance
- §3 Per-utterance round: client emits → assistant runs
PIPELINE-1 lifecycle → emits N responses, each carrying
session at emission point → client receives and updates
- §4 Client-side merge rules — minimal and permissive:
- session_id is the only key (no routing-data filtering;
session_id uniquely identifies channels)
- every assistant-emitted Message carries a valid session
- ovos.utterance.handled is the canonical convergence point
(no new topic needed)
- §5 Resumption is implicit. A client re-emits a previously-
used session_id with held state at any time; orchestrator's
statelessness for named sessions makes it work uniformly.
Resumption-safe = session-resident. Per §2.4 projection
mandate, nothing component-held is non-resumption-safe in a
conformant deployment
- §6 Default-session ownership: orchestrator holds persistent
in-process state for "default"; restart drops it (acceptable
for local-device "fresh start" semantics); components keyed
on default get effective persistence within deployment
lifetime
- §7 Conformance for bus, orchestrator, component, client, and
the special-case default-session client
- §8 Non-goals — no persistence protocol, no auth, no
cross-client coordination, no lifecycle observability events
The §2.4 projection mandate has cascading effects on existing
in-flight specs (CONVERSE-1 in particular). Follow-up PRs will
land separately to revise CONVERSE-1's response-mode wait state
to project to session.response_mode as a structured field and
drop the side-band set/clear topics now made redundant.
Single file per AGENTS.md repo policy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-internal Per design feedback: the strict MUST-project rule was too tight. Real-world plugins (LLM transcripts, media playback state, learned personalization, anything tied to external resources) cannot practically project into session — the state is too large, too coupled to external resources, or too sensitive. Relax §2.4 to SHOULD-project-when-practical with an explicit MAY-internal alternative. A component that takes the internal path: - owns its state lifecycle in full (persistence, expiry, eviction, multi-orchestrator coordination if applicable) - offers best-effort resumption with no normative guarantee — a user asking "unpause the music" months later may or may not get a useful reaction, plugin's choice - MUST NOT expect other components to know its state exists Updates: - §2.4 rewritten with concrete examples (LLM history, media playback state, personalization, external-resource ties) and explicit responsibility transfer - §5.2 'projected state of every component bound by §2.4' → 'projected state of every component that elected the SHOULD-project pathway' - §5.3 rewritten: plugin-internal state is permitted with best-effort resumption; spec defines no protocol for it; resumption parity across plugins is not guaranteed - §7.3 Component conformance split: MUST list keeps in-utterance cache discipline; SHOULD list governs projection; MAY allows internal state with the responsibility/resumption caveats - §1 scope and 'builds on' list resoften language - §3.1 cross-ref likewise Both pathways are conformant; the choice is per plugin. The CONVERSE-1 converse plugin still chooses projection (its state is small and naturally session-coupled); the spec explicitly calls this out as one example of the SHOULD path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sident response_mode; drop set topics Follow-up to OVOS-SESSION-2 (PR #27). The §2.4 projection mandate ("components MUST project session-keyed state into session- resident fields they own") requires CONVERSE-1's previously plugin-internal response-mode wait state to become a session- resident field. Three coordinated changes: 1. session.response_mode (§2.2) becomes a structured object {owner_id, expires_at, auto_continue}, replacing the prior single-string per-dispatch marker. The field is now persistent session state — it rides every Message that carries the session forward, lands in the client's local store (named sessions) or orchestrator's default-session store (default), and is visible to the plugin on every inbound utterance. 2. The side-band ovos.converse.response_mode.set / .set.response topic pair is removed entirely. A handler now enters response mode by mutating session.response_mode in-place within its dispatched handler (per SESSION-2 §2.6 handler-boundary mutation) and emitting any Message that carries the mutated session forward. The same in-handler mutation pattern handles leaving response mode (omit the field). Bus surface drops from 7 topics to 5. 3. §3.2 'Asynchronous mutation via bus events' is removed entirely (no bus events mutate session under the new model). §3 now lists three in-utterance mutation pathways: §3.1 automatic-activation-on-dispatch, §3.2 (renumbered) TTL prune, §3.3 (renumbered) transformer mutation. Plus the implicit §4.2 match-phase pathway (PIPELINE-1). Plus consequent edits throughout: - §1, §4 paragraph, §5 intro reframe response mode as session-resident, not bus-event-driven - §5.1 rewritten as in-handler-mutation pattern - §5.2 simplified: plugin reads session.response_mode directly, no plugin-internal state lookup - §5.3-§5.5 streamlined: timeout from expires_at, cancellation from in-handler removal, interrupt via framework - §9.1 orchestrator: drops set-event obligations; references SESSION-2 for session persistence - §9.2 converse plugin: holds NO cross-utterance state (§2.4 projection mandate); reads everything from session - §9.3 handler: enters/leaves response mode by session mutation, not bus events - §9.4 observer: can read session.response_mode for introspection - §10 non-goals: dedicated bus events for response mode are explicitly out - §1 build-on list: SESSION-2 added as dependency Five topics remain (down from 7): active.list pair, converse-poll pair, two dispatch topics for the reserved intent_names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
) 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>
6a882c8 to
4b4131e
Compare
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>
* 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>
- §2.3: align "default" framing with SESSION-1 §3.1 (interact with device-local session, not "originates from device") - §3.2: clarify PIPELINE-1 is authoritative for lifecycle detail - §5.2: SESSION-1 §2.5 → §2.1 (correct cross-ref for omission rule) - §6.1: restate merge rule as omission-preserve first, present-replaces second; drop "last-write-wins" framing that clashed with SESSION-1 §2.1 - §6.4: drop ovos.session.sync / ovos.session.update_default topic names; deployer-defined topic only, no normative name - §7.2: §4.4 → §4.3 (end-marker convergence point cross-ref) - §8: collapse to one-paragraph; §1 is the canonical non-goals list Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Drop §3 per-utterance round entirely (was narrative glue restating
PIPELINE-1 and §2; replaced with PIPELINE-1 §6 cross-ref at §2.2)
- §4.1 second paragraph removed ("also conformant to ignore the spec")
- §7.3 trivial MUST dropped ("treat caches as utterance-scoped")
- §2.5 second paragraph collapsed to one sentence (trust = layer-2)
- §5.2 field list replaced with "every field in SESSION-1 §3 registry"
- §6.3 cut by half; key point preserved
- §7.4 MAY list collapsed to one sentence pointing at §3 and §4
- §8 non-goals already collapsed; fix §5.2 cross-ref in it
- Renumber: old §5→§4, §6→§5, §7→§6, §8→§7; all internal refs updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
session-lifecycle.md (1)
245-246: 💤 Low valueConsider more formal pseudocode for normative merge rule.
The Python-style
oroperator insession = match.updated_session or sessionclearly conveys the intent (apply if non-null), but normative specifications typically use more formal conditional language.📝 More formal alternative
- return a `Match.updated_session` per PIPELINE-1 §4.2; the - orchestrator MUST apply it as `session = match.updated_session - or session` immediately on a non-null match; + return a `Match.updated_session` per PIPELINE-1 §4.2; the + orchestrator MUST apply it immediately on a non-null match: + if `match.updated_session` is not null, replace `session` + with `match.updated_session`;🤖 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 `@session-lifecycle.md` around lines 245 - 246, The normative text uses Python's `or` in `session = match.updated_session or session`, which is informal; replace it with a clear conditional statement using the existing symbols: state that the orchestrator MUST set `session` to `match.updated_session` only when `match.updated_session` is non-null and otherwise leave `session` unchanged (e.g., "If `match.updated_session` is non-null, set `session` to `match.updated_session`; otherwise retain the current `session`"), ensuring the rule remains normative and unambiguous for `orchestrator`, `match`, and `session`.
🤖 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.
Nitpick comments:
In `@session-lifecycle.md`:
- Around line 245-246: The normative text uses Python's `or` in `session =
match.updated_session or session`, which is informal; replace it with a clear
conditional statement using the existing symbols: state that the orchestrator
MUST set `session` to `match.updated_session` only when `match.updated_session`
is non-null and otherwise leave `session` unchanged (e.g., "If
`match.updated_session` is non-null, set `session` to `match.updated_session`;
otherwise retain the current `session`"), ensuring the rule remains normative
and unambiguous for `orchestrator`, `match`, and `session`.
…s-client disambiguation - Intro: add TRANSFORM-1 as fifth companion spec; its six transformer hooks are the normative session-mutation boundaries cited in §2.6 - §1 non-goals: cross-client session sharing note now mentions that MSG-1 source/destination routing is the layer-2 disambiguation mechanism for which client owns a given session_id Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Specs must be timeless and standalone — no named implementation projects. Replace two prose references to ovos-core and SessionManager.default_session with implementation-agnostic descriptions. Co-Authored-By: Claude Sonnet 4.6 <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>
…sident response_mode; drop set topics Follow-up to OVOS-SESSION-2 (PR #27). The §2.4 projection mandate ("components MUST project session-keyed state into session- resident fields they own") requires CONVERSE-1's previously plugin-internal response-mode wait state to become a session- resident field. Three coordinated changes: 1. session.response_mode (§2.2) becomes a structured object {owner_id, expires_at, auto_continue}, replacing the prior single-string per-dispatch marker. The field is now persistent session state — it rides every Message that carries the session forward, lands in the client's local store (named sessions) or orchestrator's default-session store (default), and is visible to the plugin on every inbound utterance. 2. The side-band ovos.converse.response_mode.set / .set.response topic pair is removed entirely. A handler now enters response mode by mutating session.response_mode in-place within its dispatched handler (per SESSION-2 §2.6 handler-boundary mutation) and emitting any Message that carries the mutated session forward. The same in-handler mutation pattern handles leaving response mode (omit the field). Bus surface drops from 7 topics to 5. 3. §3.2 'Asynchronous mutation via bus events' is removed entirely (no bus events mutate session under the new model). §3 now lists three in-utterance mutation pathways: §3.1 automatic-activation-on-dispatch, §3.2 (renumbered) TTL prune, §3.3 (renumbered) transformer mutation. Plus the implicit §4.2 match-phase pathway (PIPELINE-1). Plus consequent edits throughout: - §1, §4 paragraph, §5 intro reframe response mode as session-resident, not bus-event-driven - §5.1 rewritten as in-handler-mutation pattern - §5.2 simplified: plugin reads session.response_mode directly, no plugin-internal state lookup - §5.3-§5.5 streamlined: timeout from expires_at, cancellation from in-handler removal, interrupt via framework - §9.1 orchestrator: drops set-event obligations; references SESSION-2 for session persistence - §9.2 converse plugin: holds NO cross-utterance state (§2.4 projection mandate); reads everything from session - §9.3 handler: enters/leaves response mode by session mutation, not bus events - §9.4 observer: can read session.response_mode for introspection - §10 non-goals: dedicated bus events for response mode are explicitly out - §1 build-on list: SESSION-2 added as dependency Five topics remain (down from 7): active.list pair, converse-poll pair, two dispatch topics for the reserved intent_names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sident response_mode; drop set topics Follow-up to OVOS-SESSION-2 (PR #27). The §2.4 projection mandate ("components MUST project session-keyed state into session- resident fields they own") requires CONVERSE-1's previously plugin-internal response-mode wait state to become a session- resident field. Three coordinated changes: 1. session.response_mode (§2.2) becomes a structured object {owner_id, expires_at, auto_continue}, replacing the prior single-string per-dispatch marker. The field is now persistent session state — it rides every Message that carries the session forward, lands in the client's local store (named sessions) or orchestrator's default-session store (default), and is visible to the plugin on every inbound utterance. 2. The side-band ovos.converse.response_mode.set / .set.response topic pair is removed entirely. A handler now enters response mode by mutating session.response_mode in-place within its dispatched handler (per SESSION-2 §2.6 handler-boundary mutation) and emitting any Message that carries the mutated session forward. The same in-handler mutation pattern handles leaving response mode (omit the field). Bus surface drops from 7 topics to 5. 3. §3.2 'Asynchronous mutation via bus events' is removed entirely (no bus events mutate session under the new model). §3 now lists three in-utterance mutation pathways: §3.1 automatic-activation-on-dispatch, §3.2 (renumbered) TTL prune, §3.3 (renumbered) transformer mutation. Plus the implicit §4.2 match-phase pathway (PIPELINE-1). Plus consequent edits throughout: - §1, §4 paragraph, §5 intro reframe response mode as session-resident, not bus-event-driven - §5.1 rewritten as in-handler-mutation pattern - §5.2 simplified: plugin reads session.response_mode directly, no plugin-internal state lookup - §5.3-§5.5 streamlined: timeout from expires_at, cancellation from in-handler removal, interrupt via framework - §9.1 orchestrator: drops set-event obligations; references SESSION-2 for session persistence - §9.2 converse plugin: holds NO cross-utterance state (§2.4 projection mandate); reads everything from session - §9.3 handler: enters/leaves response mode by session mutation, not bus events - §9.4 observer: can read session.response_mode for introspection - §10 non-goals: dedicated bus events for response mode are explicitly out - §1 build-on list: SESSION-2 added as dependency Five topics remain (down from 7): active.list pair, converse-poll pair, two dispatch topics for the reserved intent_names. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
OVOS-SESSION-2 — Session Lifecycle and State Ownership
Companion lifecycle spec to OVOS-SESSION-1 (which defines only wire shape and explicitly defers lifecycle). Closes the "session lifecycle" gap APPENDIX §7 has tracked since the spec set's inception, and is the normative reference OVOS-CONTEXT-1 and OVOS-CONVERSE-1 have been forward-referencing. Companion to issue #26.
Core principles
session_id == \"default\"— the local-device session is the one exception (§6). Formalises current ovos-coreSessionManager.default_session.session_idalone, uses for next round.session_id; statelessness makes it work across arbitrary elapsed time and across orchestrator restart.Section outline
session_idis the only key;ovos.utterance.handledis the canonical convergence point — no new topic)In-utterance mutation boundaries (§2.6)
Session is mutated in-place only at:
Match.updated_sessionper OVOS-PIPELINE-1 §4.2)Bus events are asynchronous and not part of the utterance lifecycle — they MAY carry session updates that land on subsequent utterances, but MUST NOT be expected to mutate session in the current utterance.
Cross-references
Match.updated_session,ovos.utterance.handledend-marker.intent_contextfield already follows the model.Cascading edits (separate follow-up PRs)
The §2.4 projection mandate forces these revisions, each in its own one-file PR:
session.response_modebecomes structured{owner_id, expires_at, auto_continue_remaining}populated by plugin viaMatch.updated_session. The side-bandovos.converse.response_mode.set/.set.responsetopics become redundant. Bus surface drops from 7 to 5 topics. §3.2 "async bus events" section removed.Status
Draft. Single new file:
session-lifecycle.md. Refs: issue #26.Summary by CodeRabbit