From 3858a7e6963e4f999933c2e297545c881d644feb Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 19:34:40 +0100 Subject: [PATCH 1/8] =?UTF-8?q?SESSION-2=20=C2=A72.6,=20=C2=A72.7,=20?= =?UTF-8?q?=C2=A76=E2=80=93=C2=A77:=20add=20ovos.session.sync=20and=20muta?= =?UTF-8?q?tion=20discipline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §2.6: add session mutation discipline rule — handlers SHOULD NOT mutate session unless necessary or prescribed by another spec; forward-ref §2.7 for out-of-utterance propagation - §2.7 (new): define ovos.session.sync — explicit broadcast for session updates outside the utterance lifecycle; consumer obligations for orchestrator (MUST merge for default session) and clients (SHOULD merge for named sessions); SHOULD NOT emit gratuitously - §6.2: orchestrator MUST merge ovos.session.sync for default session - §6.3: component SHOULD NOT mutate session gratuitously; MUST use ovos.session.sync when out-of-utterance propagation is needed - §7 (new): bus topics table with ovos.session.sync - §8: renumber former §7 Non-goals; fix stale §5.2 cross-ref to §5.3 Co-Authored-By: Claude Sonnet 4.6 --- ovos-session-2.md | 95 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 6 deletions(-) diff --git a/ovos-session-2.md b/ovos-session-2.md index db9ab4c5..9fa16dac 100644 --- a/ovos-session-2.md +++ b/ovos-session-2.md @@ -61,10 +61,11 @@ The key words **MUST**, **MUST NOT**, **SHOULD**, **SHOULD NOT**, This specification defines: - the **state-ownership model** (§2) — who holds session state, - what is permitted to mutate it, and when components SHOULD + what is permitted to mutate it, when components SHOULD project their cross-utterance state into session-resident - fields vs hold it internally with best-effort resumption - semantics; + fields vs hold it internally, the session mutation discipline + (§2.6), and the explicit out-of-utterance sync mechanism + `ovos.session.sync` (§2.7); - the **client-side merge rules** (§3) — how a client tracks session updates from assistant-emitted Messages, keyed on `session_id` alone; @@ -257,6 +258,18 @@ happen only at these boundaries: `response` (OVOS-MSG-1 §5) carry the mutated session forward. +**Session mutation discipline.** A handler SHOULD NOT mutate +session fields unless the mutation is necessary for the +handler's function or is explicitly prescribed by another +specification. Incidental mutations add state that clients and +observers must track, increase the risk of session-state races +in multi-component deployments, and make session evolution +harder to reason about. When another spec prescribes a +mutation (e.g. a handler removing itself from +`session.active_handlers` per OVOS-STOP-1 §4.4), that +prescription is the authority; this discipline rule does not +override it. + Bus events emitted *outside* these boundaries — the asynchronous, normal-event-handler kind that any component may emit at any time — **MUST NOT** be expected to mutate session @@ -269,6 +282,54 @@ session is received by the client and merged per §3), but **MUST NOT** be expected to affect the utterance during which it was emitted. +A component that needs to propagate a session update outside +the normal utterance lifecycle SHOULD use `ovos.session.sync` +(§2.7) rather than relying on an unrelated Message to carry +the update incidentally. + +### 2.7 Out-of-utterance session sync — `ovos.session.sync` + +When a component needs to broadcast a session update outside +the utterance lifecycle it MUST do so via the dedicated topic +`ovos.session.sync`. The Message carries `Message.context.session` +with the updated snapshot per OVOS-MSG-1; the `session_id` +within that carrier identifies the session being updated. + +`ovos.session.sync` is a plain broadcast — not a PIPELINE-1 +§7 dispatch, not a round-trip. It does not fire the +handler-lifecycle trio and does not activate any owner. + +**When to emit.** A component MAY emit `ovos.session.sync` +at any time for any reason. It SHOULD do so only when: + +- the session update cannot ride on a Message already being + emitted in the normal flow (i.e. no `speak`, `forward`, or + other emission is available to carry it); or +- another specification explicitly prescribes using it for a + specific state change (opportunistic self-removal from + `session.active_handlers`, `session.converse_handlers`, + or equivalent). + +A component SHOULD NOT emit `ovos.session.sync` gratuitously. +The normal derivation chain (§2.6) is the preferred +propagation path; `ovos.session.sync` exists for cases where +no in-utterance emission is available. + +**Consumer obligations.** + +- The **orchestrator** MUST merge a received + `ovos.session.sync` carrying `session_id == "default"` into + its default-session store (§5) on receipt. The merge follows + §5.1's field-replacement rule: present fields in the synced + snapshot replace stored values; absent fields leave stored + values unchanged. The orchestrator MUST NOT apply the sync + to the session of an utterance already in-flight on that + `session_id` — it takes effect on the next inbound + utterance. +- **Clients** SHOULD update their local session store when + they observe `ovos.session.sync` carrying a `session_id` + matching their own, using the same merge semantics as §3. + --- ## 3. Client-side merge rules @@ -467,7 +528,11 @@ An orchestrator that claims conformance to this specification the §2.6 boundaries dictate mutation; - emit the universal end-marker `ovos.utterance.handled` carrying the final round session (PIPELINE-1 §9), as the - client-side convergence point of §3.3. + client-side convergence point of §3.3; +- merge `ovos.session.sync` Messages carrying + `session_id == "default"` into its default-session store + per §2.7, on receipt, without applying the update to any + utterance already in-flight on that session. An orchestrator **MUST NOT** require any client to declare session-start / session-end / session-id-allocation events @@ -498,6 +563,12 @@ session state in the current utterance (§2.6). It MAY emit such events to communicate with other components; their effect on session, if any, lands on subsequent utterances. +A component **SHOULD NOT** mutate session fields in its handler +unless the mutation is necessary or prescribed by another +specification (§2.6 discipline rule). When a session update +must be propagated outside the normal utterance flow, the +component MUST use `ovos.session.sync` (§2.7). + ### 6.4 Client A **client** (any participant on the user side of the bus @@ -531,10 +602,22 @@ default-session store *is* that state for the local device. --- -## 7. Non-goals +## 7. Bus topics + +| Topic | Direction | Purpose | +|-------|-----------|---------| +| `ovos.session.sync` | component → all | Broadcast an explicit session update outside the utterance lifecycle (§2.7). Carries `Message.context.session` with the updated snapshot. | + +No other normative bus topic is defined by this specification. +The per-utterance session propagation (§2.6) and end-marker +(§3.3) travel on topics owned by OVOS-PIPELINE-1. + +--- + +## 8. Non-goals See §1 for the full list of non-goals. This section adds one clarification: **default-session persistence across orchestrator -restart** is not defined here. §5.2 makes restart-loss +restart** is not defined here. §5.3 makes restart-loss explicit and intentional; persistence is deployer policy if desired. From e43d99557303db360d1fb2dd027a5bf6efcd9fd7 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 19:43:47 +0100 Subject: [PATCH 2/8] =?UTF-8?q?SESSION-2=20=C2=A72.7,=20=C2=A76.2:=20orche?= =?UTF-8?q?strator=20MUST=20honor=20sync=20in=20terminal=20events?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ovos.session.sync must be merged into the working session snapshot immediately on receipt; the merged state MUST be reflected in the handler-lifecycle .complete and ovos.utterance.handled terminal events. Remove the incorrect 'MUST NOT apply to in-flight utterance' rule — the sync arrives at a handler boundary (§2.6) and must flow forward. Co-Authored-By: Claude Sonnet 4.6 --- ovos-session-2.md | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/ovos-session-2.md b/ovos-session-2.md index 9fa16dac..5bd91117 100644 --- a/ovos-session-2.md +++ b/ovos-session-2.md @@ -318,14 +318,20 @@ no in-utterance emission is available. **Consumer obligations.** - The **orchestrator** MUST merge a received - `ovos.session.sync` carrying `session_id == "default"` into - its default-session store (§5) on receipt. The merge follows - §5.1's field-replacement rule: present fields in the synced - snapshot replace stored values; absent fields leave stored - values unchanged. The orchestrator MUST NOT apply the sync - to the session of an utterance already in-flight on that - `session_id` — it takes effect on the next inbound - utterance. + `ovos.session.sync` into its working session snapshot for + the affected `session_id`. The merge follows §5.1's + field-replacement rule: present fields in the synced + snapshot replace current values; absent fields leave current + values unchanged. For `session_id == "default"` the working + snapshot is the default-session store (§5); for named + sessions it is the transient per-utterance session in + progress (§2.2). The orchestrator MUST reflect the merged + state in any terminal events it subsequently emits for the + same utterance — specifically the handler-lifecycle + `.complete` event (OVOS-PIPELINE-1 §8) and the universal + end-marker `ovos.utterance.handled` (PIPELINE-1 §9.5) — + so that clients and observers receive a session snapshot that + includes the sync update. - **Clients** SHOULD update their local session store when they observe `ovos.session.sync` carrying a `session_id` matching their own, using the same merge semantics as §3. @@ -529,10 +535,11 @@ An orchestrator that claims conformance to this specification - emit the universal end-marker `ovos.utterance.handled` carrying the final round session (PIPELINE-1 §9), as the client-side convergence point of §3.3; -- merge `ovos.session.sync` Messages carrying - `session_id == "default"` into its default-session store - per §2.7, on receipt, without applying the update to any - utterance already in-flight on that session. +- merge `ovos.session.sync` Messages per §2.7 into the + working session snapshot for the affected `session_id` on + receipt, and reflect the merged state in the subsequent + handler-lifecycle `.complete` and `ovos.utterance.handled` + terminal events for the same utterance. An orchestrator **MUST NOT** require any client to declare session-start / session-end / session-id-allocation events From 53e84308b30b818209cc154a46a2fa56bd233965 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 19:45:21 +0100 Subject: [PATCH 3/8] SESSION-2: three pre-merge fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §2.3: remove duplicate sentence - §5.1: fix stale PIPELINE-1 §5.2 cross-ref → §4.2 - §2.7, §6.3: MUST → SHOULD for ovos.session.sync usage; emitting any session-carrying Message is also valid per §2.6 Co-Authored-By: Claude Sonnet 4.6 --- ovos-session-2.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/ovos-session-2.md b/ovos-session-2.md index 5bd91117..5e46ed05 100644 --- a/ovos-session-2.md +++ b/ovos-session-2.md @@ -159,7 +159,6 @@ This is the one exception to §2.2. The local device is a client of the orchestrator that runs in the same process tree as the orchestrator itself; making the orchestrator hold its state is the simplest representation of that physical -co-location. This is the simplest representation of that physical co-location. Behaviour rules for the default-session store are in §5. @@ -290,7 +289,7 @@ the update incidentally. ### 2.7 Out-of-utterance session sync — `ovos.session.sync` When a component needs to broadcast a session update outside -the utterance lifecycle it MUST do so via the dedicated topic +the utterance lifecycle it SHOULD use the dedicated topic `ovos.session.sync`. The Message carries `Message.context.session` with the updated snapshot per OVOS-MSG-1; the `session_id` within that carrier identifies the session being updated. @@ -455,7 +454,7 @@ orchestrator operation: default state on the dispatch they receive; - session mutations during the lifecycle (transformer boundaries §2.6, `Match.updated_session` per PIPELINE-1 - §5.2, in-handler mutations) propagate into the store + §4.2, in-handler mutations) propagate into the store through the standard derivation chain. The merge semantics for inbound default-session Messages follow @@ -574,7 +573,7 @@ A component **SHOULD NOT** mutate session fields in its handler unless the mutation is necessary or prescribed by another specification (§2.6 discipline rule). When a session update must be propagated outside the normal utterance flow, the -component MUST use `ovos.session.sync` (§2.7). +component SHOULD use `ovos.session.sync` (§2.7). ### 6.4 Client From 168b5b85d431017ddc4934ec61f33fb9eee357fc Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 19:55:07 +0100 Subject: [PATCH 4/8] =?UTF-8?q?SESSION-2=20=C2=A72.7:=20session=20sync=20p?= =?UTF-8?q?ayload=20in=20Message.data=20not=20context?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ovos.session.sync carries the updated session in Message.data.session (explicit payload); Message.context.session remains the ambient routing carrier per MSG-1. Add data shape table. Update consumer obligations to reference data.session explicitly. Co-Authored-By: Claude Sonnet 4.6 --- ovos-session-2.md | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/ovos-session-2.md b/ovos-session-2.md index 5e46ed05..a2151110 100644 --- a/ovos-session-2.md +++ b/ovos-session-2.md @@ -290,9 +290,18 @@ the update incidentally. When a component needs to broadcast a session update outside the utterance lifecycle it SHOULD use the dedicated topic -`ovos.session.sync`. The Message carries `Message.context.session` -with the updated snapshot per OVOS-MSG-1; the `session_id` -within that carrier identifies the session being updated. +`ovos.session.sync`. The updated session snapshot is the +**payload** of the Message — carried in `Message.data` as a +`session` object, not in `Message.context.session`. +`Message.context.session` remains the ambient carrier (per +OVOS-MSG-1) and continues to identify the session for routing; +`Message.data.session` is the explicit sync content. + +`Message.data` shape: + +| Key | Type | Required | Meaning | +|-----|------|----------|---------| +| `session` | object | yes | The updated session snapshot. Follows SESSION-1 wire shape; omitted fields leave the receiver's current values unchanged (§5.1 merge rule). | `ovos.session.sync` is a plain broadcast — not a PIPELINE-1 §7 dispatch, not a round-trip. It does not fire the @@ -316,10 +325,10 @@ no in-utterance emission is available. **Consumer obligations.** -- The **orchestrator** MUST merge a received - `ovos.session.sync` into its working session snapshot for - the affected `session_id`. The merge follows §5.1's - field-replacement rule: present fields in the synced +- The **orchestrator** MUST merge `Message.data.session` from + a received `ovos.session.sync` into its working session + snapshot for the affected `session_id`. The merge follows + §5.1's field-replacement rule: present fields in the synced snapshot replace current values; absent fields leave current values unchanged. For `session_id == "default"` the working snapshot is the default-session store (§5); for named @@ -332,8 +341,10 @@ no in-utterance emission is available. so that clients and observers receive a session snapshot that includes the sync update. - **Clients** SHOULD update their local session store when - they observe `ovos.session.sync` carrying a `session_id` - matching their own, using the same merge semantics as §3. + they observe `ovos.session.sync` whose `Message.context.session` + carries a `session_id` matching their own, merging + `Message.data.session` using the same field-replacement + semantics as §3. --- @@ -612,7 +623,7 @@ default-session store *is* that state for the local device. | Topic | Direction | Purpose | |-------|-----------|---------| -| `ovos.session.sync` | component → all | Broadcast an explicit session update outside the utterance lifecycle (§2.7). Carries `Message.context.session` with the updated snapshot. | +| `ovos.session.sync` | component → all | Broadcast an explicit session update outside the utterance lifecycle (§2.7). Updated snapshot in `Message.data.session`; `session_id` identified via `Message.context.session` per MSG-1. | No other normative bus topic is defined by this specification. The per-utterance session propagation (§2.6) and end-marker From 97046d19a395dd968b8979ce3ce2becbbc314cca Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 19:59:06 +0100 Subject: [PATCH 5/8] =?UTF-8?q?SESSION-2=20=C2=A72.7:=20ovos.session.sync?= =?UTF-8?q?=20MUST=20use=20.forward=20inside=20a=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ensures routing metadata is preserved so the sync reaches remote clients through layer-2 transports. Without .forward the message carries no routing context and is invisible to satellite/gateway clients. Co-Authored-By: Claude Sonnet 4.6 --- ovos-session-2.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ovos-session-2.md b/ovos-session-2.md index a2151110..047be71d 100644 --- a/ovos-session-2.md +++ b/ovos-session-2.md @@ -307,6 +307,16 @@ OVOS-MSG-1) and continues to identify the session for routing; §7 dispatch, not a round-trip. It does not fire the handler-lifecycle trio and does not activate any owner. +A handler emitting `ovos.session.sync` from within a +dispatched handler invocation MUST derive the Message via +`forward` (OVOS-MSG-1 §5). `forward` preserves the routing +metadata of the inbound dispatch, ensuring the sync reaches +the originating client through any layer-2 transport +(satellite, gateway, or equivalent) that routes by those +fields. An `ovos.session.sync` emitted without `forward` +inside a handler carries no routing metadata and will not +reach remote clients. + **When to emit.** A component MAY emit `ovos.session.sync` at any time for any reason. It SHOULD do so only when: From e59e78729ea78bf1f4ecdddfb1c0b2cd78a25186 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 20:02:54 +0100 Subject: [PATCH 6/8] =?UTF-8?q?PIPELINE-1=20=C2=A710.2:=20response=20MUST?= =?UTF-8?q?=20use=20.reply=20derivation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consistent with STOP-1 §4.2 and CONVERSE-1 §4.2 — all request/response round-trips now explicitly name the MSG-1 derivation to ensure routing metadata is preserved through layer-2 transports. Co-Authored-By: Claude Sonnet 4.6 --- ovos-pipeline-1.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ovos-pipeline-1.md b/ovos-pipeline-1.md index 68e2ba45..16c56e22 100644 --- a/ovos-pipeline-1.md +++ b/ovos-pipeline-1.md @@ -1186,7 +1186,11 @@ a `pipeline_id` from any of these sources can query it directly. ### 10.2 Response payload -The plugin **MUST** reply with the currently-loaded intent set: +The plugin **MUST** emit the response derived via `reply` +(OVOS-MSG-1 §5.2), so that routing metadata is preserved and +the response reaches the requester through any layer-2 +transport. The response carries the currently-loaded intent +set: ```json { From 85fe1b28a597944a17310667119c6c1c7d9b438a Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 20:04:36 +0100 Subject: [PATCH 7/8] =?UTF-8?q?appendix/patterns:=20add=20=C2=A73.1.2=20fo?= =?UTF-8?q?rward-vs-reply=20discipline=20note?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New section between the single-flip routing model (§3.1.1) and no- central-correlation (now §3.1.3): explains when to use .forward (same direction as the source dispatch) vs .reply (back toward the requester), why the distinction matters for layer-2 transports, and a cross-spec table showing consistent application across all five relevant emissions. Renumber former §3.1.2/§3.1.3 to §3.1.3/§3.1.4; update intro and cross-refs accordingly. Co-Authored-By: Claude Sonnet 4.6 --- appendix/patterns.md | 59 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 9 deletions(-) diff --git a/appendix/patterns.md b/appendix/patterns.md index 49cb3e47..b32d7273 100644 --- a/appendix/patterns.md +++ b/appendix/patterns.md @@ -20,10 +20,12 @@ the bus is not just an internal transport — it is the **substrate higher-level systems plug into without modifying the assistant core**. Two mechanics make that work: **single-flip routing** (§3.1.1), which keeps the routing pair -correct end-to-end without per-component effort; and **no -central state or correlation** (§3.1.2), which makes layer-2 -systems composable. HiveMind is the canonical example of what -both together enable (§3.1.3). +correct end-to-end without per-component effort; **forward vs +reply discipline** (§3.1.2), which ensures non-dispatch +emissions are routed correctly through layer-2 transports; and +**no central state or correlation** (§3.1.3), which makes +layer-2 systems composable. HiveMind is the canonical example +of what all three together enable (§3.1.4). #### 3.1.1 The single-flip routing model @@ -83,7 +85,45 @@ Implementers using `.reply` where `.forward` is appropriate produce mis-routed messages that work in local tests but silently break layer-2 routing. -#### 3.1.2 No central correlation, no central state +#### 3.1.2 Choosing `forward` vs `reply` for non-dispatch emissions + +The single-flip model above applies to the dispatch path, but the +same choice arises whenever any component emits a Message derived +from one it received. The rule is: + +- **Use `forward`** when the new Message travels in the **same + direction** as the source — toward the same destination. A handler + emitting `speak`, a session sync, or a lifecycle event during a + dispatch uses `forward`; the user-side client receives it because + the `destination` was already set by the earlier `reply` flip. +- **Use `reply`** when the new Message travels **back toward the + sender** of the source — a responder answering a requester. A + skill responding to `ovos.stop.ping`, a handler answering a + `.converse.request` poll, or a plugin answering an + introspection request all use `reply`; the flip routes the answer + back to whoever asked. + +The practical consequence for layer-2 systems (satellites, gateways, +HiveMind nodes): routing metadata is the only mechanism a transport +has to decide where a Message goes. A component that uses `reply` +where `forward` is correct sends the Message back at itself instead +of toward the user; a component that uses `forward` where `reply` is +correct broadcasts with no destination instead of targeting the +requester. Both mistakes work on a single-node local bus (where +everything is broadcast anyway) and silently fail in layer-2 +deployments. + +The specs enforce this consistently: + +| Emission | Derivation | Reason | +|---|---|---| +| Handler lifecycle trio (PIPELINE-1 §8) | `forward` | Travels toward the client alongside the dispatch | +| `ovos.session.sync` from a handler (SESSION-2 §2.7) | `forward` | Session update travels toward the client | +| `ovos.stop.pong` (STOP-1 §4.2) | `reply` | Response back to the stop plugin that pinged | +| `.converse.response` (CONVERSE-1 §4.2) | `reply` | Response back to the converse plugin that polled | +| Pipeline introspection response (PIPELINE-1 §10.2) | `reply` | Response back to the observer that requested | + +#### 3.1.3 No central correlation, no central state The bus is **fully asynchronous**. OVOS does not centrally correlate request/response chains, and does not centrally @@ -121,7 +161,7 @@ of conversational state. The async-by-default model means those future specs only need to define *what* the state is, not *how* it travels. -#### 3.1.3 Layer-2 substrates +#### 3.1.4 Layer-2 substrates The single-flip routing model and the no-central-state design make layer-2 federation composable without modifying @@ -257,9 +297,10 @@ to and from an external transport, and either operates entirely external (Wyoming-style audio / STT / TTS services talking over TCP to a bridge that proxies the OVOS bus) or remotes the whole bus (HiveMind-style layer-2 substrates). The -single-flip routing of §3.1.1 and the no-central-state stance -of §3.1.2 are what make the bus-boundary adapter feasible -without modifying the assistant core. +single-flip routing of §3.1.1, the forward/reply discipline +of §3.1.2, and the no-central-state stance of §3.1.3 are what +make the bus-boundary adapter feasible without modifying the +assistant core. #### Per-protocol notes From f3934ac60474f3e97e062d9d3b77cf2a12a21891 Mon Sep 17 00:00:00 2001 From: JarbasAi Date: Wed, 27 May 2026 20:05:46 +0100 Subject: [PATCH 8/8] =?UTF-8?q?appendix/reference=20=C2=A76.5:=20add=20for?= =?UTF-8?q?ward-vs-reply=20derivation=20cheat-sheet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cross-spec table of every normative .forward / .reply usage with the decision rule. Companion to patterns.md §3.1.2 narrative. Co-Authored-By: Claude Sonnet 4.6 --- appendix/reference.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/appendix/reference.md b/appendix/reference.md index dd9f77bd..677ddbf4 100644 --- a/appendix/reference.md +++ b/appendix/reference.md @@ -144,3 +144,21 @@ Three properties hold across all four: All four surfaces share the `ovos..` prefix; verb segments vary by domain (some nest, some don't). The uniformity is in the namespace, not in a fixed depth. + +### 6.5 Message derivation cheat-sheet — `forward` vs `reply` + +Use `forward` when a Message travels in the **same direction** +as the source (handler → client). Use `reply` when a Message +travels **back toward the sender** of the source (responder → +requester). Using the wrong derivation produces messages that +work on a single-node local bus but silently mis-route through +layer-2 transports (see appendix/patterns.md §3.1.2). + +| Emission | Derivation | Rule | +|---|---|---| +| Handler `speak`, GUI events, session mutations during dispatch (PIPELINE-1 §7) | `forward` | Same direction as the inbound dispatch | +| Handler-lifecycle trio `.start` / `.complete` / `.error` (PIPELINE-1 §8) | `forward` | Same direction as the inbound dispatch | +| `ovos.session.sync` emitted inside a handler (SESSION-2 §2.7) | `forward` | Session update travels toward the originating client | +| `ovos.stop.pong` (STOP-1 §4.2) | `reply` | Skill answers back to the stop plugin that sent the ping | +| `.converse.response` (CONVERSE-1 §4.2) | `reply` | Owner answers back to the converse plugin that polled | +| Pipeline introspection response (PIPELINE-1 §10.2) | `reply` | Plugin answers back to the observer that requested |