fix: stop emitting ovos.utterance.handled when ovos-core owns it (PIPELINE-1 §9.5)#442
Conversation
PIPELINE-1 §9.5 makes ovos.utterance.handled the orchestrator's universal end-marker, emitted by ovos-core on EVERY terminal path (matched included). ovos-core began emitting it on the matched path in 2.3.0 (the change landed on the 2.2.x line; by semver the first release carrying it is the next minor, 2.3.0a1). Guard the framework's matched-path emission on a version check so it only fires for an older/absent core during the migration window; with a core that owns it the framework no longer double-emits. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Warning Review limit reached
More reviews will be available in 33 minutes and 38 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more credits in the billing tab to continue. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greetings! The CI pipeline has delivered its findings. 🏗️I've aggregated the results of the automated checks for this PR below. 📋 Repo HealthHow's the repo's pulse? Let's take a look. 💓 ✅ All required files present. Latest Version: ✅ ⚖️ License CheckI've checked the license history of this repo. 📜 ✅ No license violations found. Policy: Apache 2.0 (universal donor). StrongCopyleft / NetworkCopyleft / WeakCopyleft / Other / Error categories fail. MPL allowed. 🔍 LintAnother day, another successful automated run. 🌅 ❌ ruff: issues found — see job log 🔒 Security (pip-audit)Ensuring our defenses are strong against vulnerabilities. 🏰 ✅ No known vulnerabilities found (72 packages scanned). 🔨 Build TestsThe build results are looking solid. 💎 ✅ All versions pass
From the digital workshop of OpenVoiceOS. 🛠️ |
… guard The rebase onto origin/dev reverted PR #440 (HandlerLifecycle util delegation) and PR #442 (ovos.utterance.handled version guard) in ovos.py, falling back to the pre-refactor manual emission that mutates the caller's message.context. Restore both: - _on_event_start/_end/_error delegate to HandlerLifecycle(...).{start, complete,error}() from ovos_bus_client.handler (PR #440). The util stamps skill_id on the forwarded copy rather than mutating the caller's message, so the original message.context is left untouched. - _on_event_end guards the ovos.utterance.handled emission on _core_owns_utterance_handled() (PIPELINE-1 §9.5, PR #442): only emit for an older/absent ovos-core; with core >=2.3.0a1 the orchestrator owns it on the matched path. - _on_event_error no longer mutates the shared skill_data dict; the util merges {exception: repr(error)} into the forwarded payload. Tests: - Restore the assertNotIn('skill_id', msg.context) assertion in test_on_event_start (valid again: the util does not mutate the original message context). - Fix test_on_event_end_is_intent_still_emits_utterance_handled to patch _core_owns_utterance_handled=False so it is deterministic regardless of the installed ovos-core version (the test was failing on environments with ovos-core >=2.3.0a1 installed, including origin/dev). - Add test_on_event_end_core_owns_suppresses_utterance_handled covering the >=2.3.0a1 branch (asserts the framework does NOT emit the end-marker when core owns it). Co-Authored-By: Claude <noreply@anthropic.com>
…xfails The hard CI failure on this branch was the stale test_on_event_end_is_intent_still_emits_utterance_handled: #442's _core_owns_utterance_handled() guard suppresses workshop's ovos.utterance.handled emission when ovos-core >= 2.3.0a1 (the orchestrator owns the PIPELINE-1 §9.5 end-marker on the matched path), which CI now installs. The #440 assertion is therefore stale. - test_base.py: replace the stale single test with two deterministic branches (both monkeypatch _core_owns_utterance_handled): * _defers_utterance_handled_to_core — modern core (>=2.3.0a1); workshop must NOT emit ovos.utterance.handled. * _emits_utterance_handled_legacy — absent/old core; workshop still emits (migration-window back-compat). - pyproject.toml: bump floors to the alphas carrying the singleton-registry SessionManager + §9.5 orchestrator ownership: ovos_bus_client >= 2.6.2a2, ovos-spec-tools >= 1.2.3a1, ovos-core >= 2.4.0a1 (test), ovos-adapt-parser >= 1.4.2a1 (test). The two intent-layers e2e xfails added by this PR stay: under the spec's 'every session folds' model (OVOS-CONTEXT-1 §4 / OVOS-SESSION-1), the legacy add_context/in-place context mutation is wiped by the next fold — the genuine fix is to migrate IntentLayers to session.intent_context via ovos.session.sync (§5.3), tracked separately.
* test: xfail intent-layers e2e tests broken on dev, unrelated to this PR test_layers_advance_in_sequence_and_gate_intents and test_inactive_layer_intents_do_not_match fail identically on a clean origin/dev checkout with none of this PR's changes applied: the layer context token is never applied (ovos_workshop/decorators/layers.py), so gated intents never match. xfail them here to unblock this PR's CI; the underlying layers bug needs its own fix in a separate PR. Co-Authored-By: Claude <noreply@anthropic.com> * fix: harden utterance.handled test (§9.5), bump pins; keep layer-e2e xfails The hard CI failure on this branch was the stale test_on_event_end_is_intent_still_emits_utterance_handled: #442's _core_owns_utterance_handled() guard suppresses workshop's ovos.utterance.handled emission when ovos-core >= 2.3.0a1 (the orchestrator owns the PIPELINE-1 §9.5 end-marker on the matched path), which CI now installs. The #440 assertion is therefore stale. - test_base.py: replace the stale single test with two deterministic branches (both monkeypatch _core_owns_utterance_handled): * _defers_utterance_handled_to_core — modern core (>=2.3.0a1); workshop must NOT emit ovos.utterance.handled. * _emits_utterance_handled_legacy — absent/old core; workshop still emits (migration-window back-compat). - pyproject.toml: bump floors to the alphas carrying the singleton-registry SessionManager + §9.5 orchestrator ownership: ovos_bus_client >= 2.6.2a2, ovos-spec-tools >= 1.2.3a1, ovos-core >= 2.4.0a1 (test), ovos-adapt-parser >= 1.4.2a1 (test). The two intent-layers e2e xfails added by this PR stay: under the spec's 'every session folds' model (OVOS-CONTEXT-1 §4 / OVOS-SESSION-1), the legacy add_context/in-place context mutation is wiped by the next fold — the genuine fix is to migrate IntentLayers to session.intent_context via ovos.session.sync (§5.3), tracked separately. * fix: un-xfail intent-layers e2e (depends on ovos-utils#396), bump ovos-utils floor The two intent-layers e2e tests xfailed in this PR are fixed upstream by OpenVoiceOS/ovos-utils#396: FakeBus.emit was folding the message session AFTER the bus handlers ran, using a pre-mutation emit-time snapshot, which wholesale-replaced the SessionManager singleton (Session.update_from is last-writer-wins) and wiped every in-place / synced session mutation the handlers made — notably handle_add_context injecting the layer token into sess.context. The #396 fix reorders FakeBus.emit to fold BEFORE handlers (matching MessageBusClient.on_message receive -> fold -> handle order) and drops the post-handler self-broadcast-back fold, so the legacy add_context -> sess.context frame mechanism survives under folding again. No workshop change is needed; the IntentLayers code is unchanged. - test_intent_layers_e2e.py: revert the xfail markers added in 2aee2bd; both tests pass once ovos-utils >= 0.13.3a2 lands. - pyproject.toml: bump ovos-utils floor to >= 0.13.3a2 (pending #396 release). --------- Co-authored-by: Claude <noreply@anthropic.com>
What
PIPELINE-1 §9.5 makes
ovos.utterance.handledthe orchestrator's universalend-marker, emitted by ovos-core on every terminal path — the matched path
included. Today the skill framework (
_on_event_end) also emits it on the matchedpath, so once core takes ownership consumers would observe the end-marker twice.
This guards the framework emission behind a semver check on the installed ovos-core:
ovos.utterance.handledonthe matched path) → framework does not emit; core owns it.
nothing regresses during the migration window.
Threshold derivation: the §9.5 matched-path emission landed on the 2.2.x line, so by
semver the first release carrying it is the next minor — 2.3.0a1.
Why a guard rather than an outright drop
The two repos release independently. A flat drop would create a window where a stack
pairs a new workshop with an old core that does not yet emit on the matched path,
silently losing the end-marker. The version guard keeps exactly one emitter at all
times; double-emits (tolerated by spec consumers anyway) never occur.
Notes
ovos.utterance.handledemission (core #788).test/unittests/test_ask_e2e.py+skills/test_base.py(the handled-assertingsuites) stay green: their env carries core 2.2.4a1 (< threshold) → framework still
emits there. 70 passed locally.
🤖 Generated with Claude Code