Skip to content

OVOS-SESSION-2: Session Lifecycle and State Ownership (draft)#27

Merged
JarbasAl merged 6 commits into
devfrom
spec/session2-lifecycle
May 26, 2026
Merged

OVOS-SESSION-2: Session Lifecycle and State Ownership (draft)#27
JarbasAl merged 6 commits into
devfrom
spec/session2-lifecycle

Conversation

@JarbasAl
Copy link
Copy Markdown
Member

@JarbasAl JarbasAl commented May 26, 2026

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

  1. Bus is stateless transport — no message-bus-level persistence.
  2. Orchestrator is stateless for named sessions — every inbound Message brings its own session; no cross-utterance state held.
  3. Orchestrator owns session_id == \"default\" — the local-device session is the one exception (§6). Formalises current ovos-core SessionManager.default_session.
  4. Components MUST project session-keyed state into session-resident fields — the §2.4 projection mandate. Drives resumption parity.
  5. Clients own their named sessions (§2.5) — persistence is the client's choice; trust is layer-2.
  6. Per-utterance round (§3) — client emits → assistant runs PIPELINE-1 lifecycle → emits N response Messages each carrying session at emission → client receives, merges keyed on session_id alone, uses for next round.
  7. Resumption is implicit (§5) — re-emit a previously-used session_id; statelessness makes it work across arbitrary elapsed time and across orchestrator restart.

Section outline

  • §1 Scope
  • §2 State-ownership model (six subsections)
  • §3 Per-utterance round
  • §4 Client-side merge rules (minimal: session_id is the only key; ovos.utterance.handled is the canonical convergence point — no new topic)
  • §5 Resumption semantics
  • §6 Default-session ownership rule
  • §7 Conformance — bus, orchestrator, component, client, default-session client
  • §8 Non-goals

In-utterance mutation boundaries (§2.6)

Session is mutated in-place only at:

  • transformer boundaries (any of OVOS-TRANSFORM-1's six hooks)
  • pipeline boundaries (Match.updated_session per OVOS-PIPELINE-1 §4.2)
  • handler boundaries (dispatched handler in-place mutation)

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

Cascading edits (separate follow-up PRs)

The §2.4 projection mandate forces these revisions, each in its own one-file PR:

Status

Draft. Single new file: session-lifecycle.md. Refs: issue #26.

Summary by CodeRabbit

  • Documentation
    • Added a Session Lifecycle and State Ownership specification defining session semantics, resumption, and client-authoritative state handling.
    • Clarified default-session persistence (ephemeral on orchestrator restart) and merge/adoption rules for clients and assistant messages.
    • Defined where and when session mutations are allowed, conformance expectations for components, and enumerated non-goals.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 26, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6e100ebb-8c32-4cc8-989f-b72bbb17bbea

📥 Commits

Reviewing files that changed from the base of the PR and between 46dcf9c and bea2235.

📒 Files selected for processing (1)
  • session-lifecycle.md

📝 Walkthrough

Walkthrough

A 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.

Changes

Session Lifecycle and State Ownership Specification

Layer / File(s) Summary
Document scope and introduction
session-lifecycle.md
Spec header, OVOS-SESSION-2 framing, RFC‑2119 normative language; scope enumerates ownership model, client merge/adoption, resumption semantics, default-session rule, and conformance; lists out-of-scope items.
Core state ownership model
session-lifecycle.md
Bus is session-opaque; orchestrator must not hold authoritative cross-utterance state for named sessions; only session_id == "default" is the orchestrator exception for in-process persistence.
Default-session and roles
session-lifecycle.md
Defines default session store behavior, component projection guidance, and client-authoritative requirement for named sessions.
Projection vs plugin-internal state
session-lifecycle.md
Distinguishes resumption-safe projected session-resident state from best-effort plugin-internal cross-utterance caches.
Session mutation boundaries and per-utterance round
session-lifecycle.md
In-utterance session mutation allowed only at transformer hooks, pipeline Match.updated_session, and handler boundaries; async bus events must not be relied on to mutate the current utterance session.
Client session merge rules and resumption semantics
session-lifecycle.md
Client-side adoption keyed by session_id; assistant-emitted messages include updated snapshots; ovos.utterance.handled recommended as canonical round-completion point; resumption via re-emission of session payload; resumption-safe fields enumerated.
Default-session store behavior
session-lifecycle.md
Default-session store updates continuously in-utterance, per-field merge where omitted inbound fields do not overwrite, orchestrator restart discards store (restart-loss), and optional diagnostic sync to clients.
Conformance requirements
session-lifecycle.md
Role-specific conformance: bus must remain session-opaque; orchestrator must be stateless for named sessions and implement default-session merge/restart behavior and emit ovos.utterance.handled; components must project/read session state and not rely on async bus events for in-round mutation; clients must include authoritative session for named sessions.
Non-goals clarification
session-lifecycle.md
Reiterates out-of-scope items and clarifies that default-session persistence across orchestrator restart is intentionally not defined (restart-loss explicit).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Possibly related PRs

Poem

🐰 A session spec takes shape with care,
Bus stays silent, default keeps a thread,
Clients hold named-state and re-emit to share,
In-utterance changes at guarded tread,
Resumption whispered — no handshake said.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically summarizes the main change: introduction of a new session lifecycle specification document that defines session state ownership and update semantics.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch spec/session2-lifecycle

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

JarbasAl added a commit that referenced this pull request May 26, 2026
…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>
JarbasAl added a commit that referenced this pull request May 26, 2026
)

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>
JarbasAl added a commit that referenced this pull request May 26, 2026
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>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5552b2e3-2690-4337-b671-60ea6585b890

📥 Commits

Reviewing files that changed from the base of the PR and between 73fec74 and 6a882c8.

📒 Files selected for processing (1)
  • session-lifecycle.md

Comment thread session-lifecycle.md Outdated
Comment thread session-lifecycle.md Outdated
JarbasAl and others added 2 commits May 26, 2026 14:34
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>
JarbasAl added a commit that referenced this pull request May 26, 2026
…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>
JarbasAl added a commit that referenced this pull request May 26, 2026
)

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>
@JarbasAl JarbasAl force-pushed the spec/session2-lifecycle branch from 6a882c8 to 4b4131e Compare May 26, 2026 13:38
JarbasAl added a commit that referenced this pull request May 26, 2026
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>
@JarbasAl JarbasAl marked this pull request as ready for review May 26, 2026 15:00
JarbasAl added a commit that referenced this pull request May 26, 2026
* 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>
JarbasAl and others added 2 commits May 26, 2026 16:42
- §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>
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
session-lifecycle.md (1)

245-246: 💤 Low value

Consider more formal pseudocode for normative merge rule.

The Python-style or operator in session = match.updated_session or session clearly 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`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7f686fa4-6e4b-43e6-93ba-00969d6703d6

📥 Commits

Reviewing files that changed from the base of the PR and between 6a882c8 and 46dcf9c.

📒 Files selected for processing (1)
  • session-lifecycle.md

JarbasAl and others added 2 commits May 26, 2026 17:43
…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>
@JarbasAl JarbasAl merged commit 37072d9 into dev May 26, 2026
1 check was pending
JarbasAl added a commit that referenced this pull request May 26, 2026
… 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>
@JarbasAl JarbasAl deleted the spec/session2-lifecycle branch May 26, 2026 22:14
JarbasAl added a commit that referenced this pull request May 27, 2026
…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>
JarbasAl added a commit that referenced this pull request May 27, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant