Skip to content

StopService: wrap handlers in HandlerLifecycle instead of ad-hoc emission#796

Merged
JarbasAl merged 5 commits into
feat/pipeline1-handler-triofrom
feat/stop-handler-lifecycle
Jun 29, 2026
Merged

StopService: wrap handlers in HandlerLifecycle instead of ad-hoc emission#796
JarbasAl merged 5 commits into
feat/pipeline1-handler-triofrom
feat/stop-handler-lifecycle

Conversation

@JarbasAl

@JarbasAl JarbasAl commented Jun 29, 2026

Copy link
Copy Markdown
Member

Summary

Replace rudimentary manual emission of lifecycle events (both the UTTERANCE_HANDLED that the orchestrator now owns and the missing mycroft.skill.handler.* trio) with the canonical HandlerLifecycle context manager from ovos-bus-client.

The HandlerLifecycle context manager consistently emits the full handler-lifecycle trio (start/complete/error) so that the dispatcher can properly track in-flight entries and fire §9.5 UTTERANCE_HANDLED at the right moment — instead of waiting for the handler timeout.

Changes

StopService (stop_service.py)

  • Remove manual message.forward(\"ovos.utterance.handled\") from handle_global_stop — the orchestrator's dispatcher emits the §9.5 end-marker
  • Wrap both handle_global_stop and handle_skill_stop in HandlerLifecycle

ConverseService (converse_service.py)

  • Wrap handle_converse in HandlerLifecycle — same self-handling pattern as StopService (pipeline match → bus event dispatch → self-handled), but was missing lifecycle signalling entirely, causing converse dispatches to only resolve on timeout

Other pipeline plugins

FallbackService does not need this — it does not self-handle its match_type (skills listen for ovos.skills.fallback.{skill_id}.request directly via workshop's automatic lifecycle wrapping).

JarbasAl and others added 2 commits June 28, 2026 22:16
Replace rudimentary manual emission of both UTTERANCE_HANDLED (which
the orchestrator now owns via IntentDispatcher._notify_terminal) and
mycroft.skill.handler.complete with the canonical HandlerLifecycle
context manager from ovos-bus-client.

HandlerLifecycle consistently emits the full handler-lifecycle trio
(start/complete/error) so that the dispatcher can properly track
in-flight entries and fire §9.5 UTTERANCE_HANDLED at the right moment.

Co-Authored-By: Claude
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c9494e25-5f4e-4dab-969a-05e385c01ead

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/stop-handler-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.

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown

Standard verification protocol finished. 📋

I've aggregated the results of the automated checks for this PR below.

🏷️ Release Preview

The release candidate is looking strong. 💪

Current: 2.2.4a3Next: 2.2.4a4

Signal Value
Label (none)
PR title StopService: wrap handlers in HandlerLifecycle instead of ad-hoc emission
Bump alpha

⚠️ No conventional commit prefix — alpha-only bump.
Suggested: fix: update the thing or feat: update the thing


🚀 Release Channel Compatibility

Predicted next version: 2.2.4a4

Channel Status Note Current Constraint
Stable Too new (must be <1.4.0) ovos-core>=1.3.1,<1.4.0
Testing Compatible ovos-core>=2.1.1,<3.0.0
Alpha Compatible ovos-core>=2.2.4a1

📋 Repo Health

I've checked the repo's strength (aka test suite robustness). 🏋️‍♂️

✅ All required files present.

Latest Version: 2.2.4a3

ovos_core/version.py — Version file
README.md — README
LICENSE — License file
pyproject.toml — pyproject.toml
⚠️ setup.py — setup.py
CHANGELOG.md — Changelog
ovos_core/version.py has valid version block markers

🔎 Type Check

Here's the report you've been waiting for. 📁

mypy: 250 error(s) found

ovos_core/main.py:24:1: error: Skipping analyzing "ovos_utils.log": module is installed, but missing library stubs or py.typed marker [import-untyped]

Errors (showing first 10/250)
test/unittests/test_skill/__init__.py:15:1: error: Skipping analyzing "ovos_workshop.skills.ovos": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:2:1: error: Skipping analyzing "ovos_config": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:3:1: error: Skipping analyzing "ovos_plugin_manager.intent_transformers": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:4:1: error: Skipping analyzing "ovos_plugin_manager.metadata_transformers": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:5:1: error: Skipping analyzing "ovos_plugin_manager.text_transformers": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:7:1: error: Skipping analyzing "ovos_plugin_manager.templates.pipeline": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:8:1: error: Skipping analyzing "ovos_utils.json_helper": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:9:1: error: Skipping analyzing "ovos_utils.log": module is installed, but missing library stubs or py.typed marker  [import-untyped]
ovos_core/transformers.py:16:9: error: Need type annotation for "loaded_plugins" (hint: "loaded_plugins: dict[<type>, <type>] = ...")  [var-annotated]
ovos_core/transformers.py:24:27: error: Function "builtins.any" is not valid as a type  [valid-type]

🔒 Security (pip-audit)

Checking for any potential security breaches. 🔓

✅ No known vulnerabilities found (110 packages scanned).

📊 Coverage

Scanning for any 'untested' alerts! 🚨

⚠️ 60.8% total coverage

⚠️ Some tests failed — coverage figures may be incomplete.

Files below 80% coverage (8 files)
File Coverage Missing lines
ovos_core/__init__.py 0.0% 7
ovos_core/__main__.py 0.0% 26
ovos_core/intent_services/__init__.py 0.0% 1
ovos_core/version.py 0.0% 18
ovos_core/skill_installer.py 42.3% 139
ovos_core/intent_services/service.py 50.0% 163
ovos_core/skill_manager.py 58.8% 173
ovos_core/transformers.py 66.0% 49

Full report: download the coverage-report artifact.

🔌 Plugin Detection

Checking if the plugin meets our contribution guidelines. ✅

⚠️ Plugin Status: WARNINGS (3)

Plugin Info:

  • Name: ovos-core
  • Description: The spiritual successor to Mycroft AI, OVOS is flexible voice assistant software that can be run almost anywhere!

OPM Detection:

Plugin Type Wheel Editable
pipeline

Entry Point Validation:

Entry Point Type Import Interface
ovos-converse-pipeline-plugin pipeline ✅ 13ms
ovos-fallback-pipeline-plugin pipeline ✅ 1ms
ovos-stop-pipeline-plugin pipeline ✅ 267ms

⊘ No settingsmeta.json
requires-python >=3.10 — running Python 3.11

Issues:

  • ⚠️ Import time for ovos-stop-pipeline-plugin is slow (267ms)
  • ⚠️ No settingsmeta.json found
  • ⚠️ No settingsmeta.json found

🔨 Build Tests

I've put your code through the build grinder. ☕

Python Build Install Tests
3.10 ⚠️
3.11 ⚠️
3.12 ⚠️
3.13 ⚠️
3.14 ⚠️

❌ 3.10: Install OK, tests failed
❌ 3.11: Install OK, tests failed
❌ 3.12: Install OK, tests failed
❌ 3.13: Install OK, tests failed
❌ 3.14: Install OK, tests failed
Check job logs for details.

⚖️ License Check

Legal eagle here! Checking those licenses. ⚖️

✅ No license violations found.

Policy: Apache 2.0 (universal donor). StrongCopyleft / NetworkCopyleft / WeakCopyleft / Other / Error categories fail. MPL allowed.

📚 Docs

Processing complete! Details follow. 📬

✅ All required documentation files present.

README.md

🌍 Locale Build

Checking if there's anything else we need to do. 📋

✅ Locale properly configured (64 files, 17 languages)

Locale directories found:

  • ovos_core/intent_services/locale

Localization coverage:

  • ovos_core/intent_services/locale: 64 files in 17 languages (eu-ES, es-es, it-it, de-de, uk-ua...)

pyproject.toml:[tool.setuptools.package-data.ovos_core] includes locale

  • intent_services/locale/*/*.voc

Build manifest: ✅ 31 locale files included in package

🔌 Skill Tests (ovoscope)

I've put the skill through its paces with live intent matching. 🏃

2/36 passed, 17 failed

❌ **TestAdaptIntent** — 0/4
Test Result
test_intent_blacklist ❌ subtests passed
test_skill_blacklist ❌ subtests passed
test_adapt_match ❌ failed
test_padatious_no_match ❌ subtests passed

test_intent_blacklist failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_skill_blacklist failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_adapt_match failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestCancelIntentMidSentence** — 0/1
Test Result
test_cancel_match ❌ subtests passed

test_cancel_match failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestConverse** — 0/1
Test Result
test_parrot_mode ❌ failed

test_parrot_mode failure:

[gw1] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestCountSkills** — 0/4
Test Result
test_count_infinity_active ❌ failed
test_count_infinity_stop_low ❌ failed
test_count_infinity_global ❌ failed
test_count ❌ failed

test_count_infinity_active failure:

[gw3] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_count_infinity_stop_low failure:

[gw1] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_count_infinity_global failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestDeactivate** — 2/3
Test Result
test_activate ✅ passed
test_deactivate ✅ passed
test_deactivate_inside_converse ❌ failed

test_deactivate_inside_converse failure:

[gw3] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestFallback** — 0/1
Test Result
test_fallback_match ❌ subtests passed

test_fallback_match failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestGlobalStopVocWithActiveSkill** — 0/1
Test Result
test_global_stop_voc_with_active_skill ❌ failed

test_global_stop_voc_with_active_skill failure:

[gw1] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestGlobalStopVocabulary** — 0/2
Test Result
test_global_stop_voc_no_active_skills ❌ failed
test_stop_voc_exact_still_works ❌ failed

test_global_stop_voc_no_active_skills failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_stop_voc_exact_still_works failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestIntentPipelineRouting** — 0/4
Test Result
test_padatious_intent_matched ❌ failed
test_high_priority_stage_handles_before_low ❌ failed
test_blacklisted_skill_falls_through_to_failure ❌ subtests passed
test_no_match_produces_intent_failure ❌ subtests passed

test_padatious_intent_matched failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_high_priority_stage_handles_before_low failure:

[gw3] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_blacklisted_skill_falls_through_to_failure failure:

[gw1] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestLangDisambiguation** — 0/4
Test Result
test_stt_lang ❌ subtests passed
test_invalid_lang_detection ❌ subtests passed
test_metadata_preferred_over_text_detection ❌ subtests passed
test_lang_text_detection ❌ subtests passed

test_stt_lang failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_invalid_lang_detection failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_metadata_preferred_over_text_detection failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestNoSkills** — 0/2
Test Result
test_complete_failure ❌ subtests passed
test_routing ❌ subtests passed

test_complete_failure failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_routing failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestPadatiousIntent** — 0/4
Test Result
test_intent_blacklist ❌ subtests passed
test_skill_blacklist ❌ subtests passed
test_padatious_match ❌ failed
test_adapt_no_match ❌ subtests passed

test_intent_blacklist failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_skill_blacklist failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_padatious_match failure:

[gw3] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestStopNoSkills** — 0/3
Test Result
test_not_exact_high ❌ subtests passed
test_not_exact_med ❌ failed
test_exact ❌ failed

test_not_exact_high failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_not_exact_med failure:

[gw1] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

test_exact failure:

[gw2] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestStopServiceAsSkill** — 0/1
Test Result
test_stop_service_emits_activate_and_stop_response ❌ failed

test_stop_service_emits_activate_and_stop_response failure:

[gw3] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python
❌ **TestStopSkillCanHandleFalse** — 0/1
Test Result
test_stop_with_active_skill_ping_pong ❌ failed

test_stop_with_active_skill_ping_pong failure:

[gw0] linux -- Python 3.11.15 /opt/hostedtoolcache/Python/3.11.15/x64/bin/python

🚌 Bus Coverage

Quantifying the invisible connections in your code. 💪

🔴 Coverage Summary

Metric Status Coverage
Listeners ██░░░░░░░░ 20.4% 51/250 handlers
Emitters ██████████ 100% 16/16 observed
Assertions ██████████ 100% 16/16 asserted

📊 Per-Skill Breakdown

Skill Listeners Observed Asserted
AdaptPipeline 4/14 (28.6%) 0/0 0/0
ConverseService 1/4 (25.0%) 0/0 0/0
DomainAdaptPipeline 4/14 (28.6%) 0/0 0/0
FallbackService 2/2 (100.0%) 0/0 0/0
HierarchicalAdaptPipeline 4/14 (28.6%) 0/0 0/0
IntentService 2/4 (50.0%) 0/0 0/0
Model2VecIntentPipeline 3/12 (25.0%) 0/0 0/0
Model2VecPrototypePipeline 3/12 (25.0%) 0/0 0/0
PadaciosoPipeline 2/11 (18.2%) 0/0 0/0
PadatiousPipeline 4/15 (26.7%) 0/0 0/0
SkillManager 0/4 (0.0%) 0/0 0/0
__core__ 6/27 (22.2%) 11/11 11/11
ovos-skill-count.openvoiceos 4/20 (20.0%) 0/0 0/0
ovos-skill-fallback-unknown.openvoiceos 3/21 (14.3%) 2/2 2/2
ovos-skill-hello-world.openvoiceos 1/24 (4.2%) 0/0 0/0
stop.openvoiceos 3/21 (14.3%) 0/0 0/0
test_activation.openvoiceos 4/27 (14.8%) 3/3 3/3
type 1/4 (25.0%) 0/0 0/0
🔍 Detailed Message Type Breakdown

AdaptPipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.keyword (Intent)
  • detach_intent
  • intent.service.adapt.get
  • intent.service.adapt.vocab.manifest.get
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • intent.service.adapt.manifest.get (877x)
  • register_intent (162x)
  • register_vocab (11273x)

ConverseService

⚠️ Uncovered Listeners:

  • converse:skill (Intent)
  • intent.service.active_skills.get
  • intent.service.skills.deactivate
    ✅ Covered Listeners:
  • intent.service.skills.activate (5x)

DomainAdaptPipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.keyword (Intent)
  • detach_intent
  • intent.service.adapt.get
  • intent.service.adapt.vocab.manifest.get
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • intent.service.adapt.manifest.get (877x)
  • register_intent (162x)
  • register_vocab (11273x)

FallbackService

✅ Covered Listeners:

  • ovos.skills.fallback.deregister (75x)
  • ovos.skills.fallback.register (41x)

HierarchicalAdaptPipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.keyword (Intent)
  • detach_intent
  • intent.service.adapt.get
  • intent.service.adapt.vocab.manifest.get
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • intent.service.adapt.manifest.get (877x)
  • register_intent (162x)
  • register_vocab (11273x)

IntentService

⚠️ Uncovered Listeners:

  • intent.service.intent.get (Intent)
  • intent.service.skills.deactivate
    ✅ Covered Listeners:
  • intent.service.pipelines.reload (205x)
  • ovos.utterance.handle (125x)

Model2VecIntentPipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.template (Intent)
  • detach_intent
  • mycroft.ready
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • padatious:register_intent (169x)
  • register_intent (162x)

Model2VecPrototypePipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.template (Intent)
  • detach_intent
  • mycroft.ready
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • padatious:register_intent (169x)
  • register_intent (162x)

PadaciosoPipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.template (Intent)
  • padatious:register_entity (Intent)
  • detach_intent
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • padatious:register_intent (169x)

PadatiousPipeline

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.register.template (Intent)
  • padatious:register_entity (Intent)
  • detach_intent
  • intent.service.padatious.entities.manifest.get
  • intent.service.padatious.get
  • ovos.entity.deregister
  • ovos.entity.register
  • ovos.skill.deregister
    ✅ Covered Listeners:
  • detach_skill (341x)
  • intent.service.padatious.manifest.get (877x)
  • mycroft.skills.train (205x)
  • padatious:register_intent (169x)

SkillManager

⚠️ Uncovered Listeners:

  • skillmanager.activate
  • skillmanager.deactivate
  • skillmanager.keep
  • skillmanager.list

__core__

⚠️ Uncovered Listeners:

  • add_context
  • clear_context
  • message
  • mycroft.ovos-skill-fallback-unknown.openvoiceos.all_loaded
  • mycroft.ovos-skill-fallback-unknown.openvoiceos.is_alive
  • mycroft.ovos-skill-fallback-unknown.openvoiceos.is_ready
  • mycroft.ovos-skill-hello-world.openvoiceos.all_loaded
  • mycroft.ovos-skill-hello-world.openvoiceos.is_alive
  • mycroft.ovos-skill-hello-world.openvoiceos.is_ready
  • mycroft.test_activation.openvoiceos.all_loaded
  • mycroft.test_activation.openvoiceos.is_alive
  • mycroft.test_activation.openvoiceos.is_ready
  • ovos-skill-fallback-unknown.openvoiceos.set
  • ovos-skill-hello-world.openvoiceos.set
  • ovos.session.sync
  • ovos.skills.fallback.force_timeout
  • ovos.skills.fallback.ovos-skill-fallback-unknown.openvoiceos
  • remove_context
  • skill.converse.get_response.disable
  • skill.converse.get_response.enable
  • test_activation.openvoiceos.set
    ✅ Covered Listeners:
  • ovos-skill-count.openvoiceos.stop.response (4x)
  • ovos.session.update_default (218x)
  • ovos.skills.fallback.pong (40x)
  • ovos.utterance.handled (229x)
  • ovos.utterance.speak (49x)
  • skill.stop.pong (4x)

📤 Emitters:

  • complete_intent_failure (Asserted ✅)
  • mycroft.audio.play_sound (Asserted ✅)
  • ovos.skills.fallback.ovos-skill-fallback-unknown.openvoiceos.request (Asserted ✅)
  • ovos.skills.fallback.ovos-skill-fallback-unknown.openvoiceos.response (Asserted ✅)
  • ovos.skills.fallback.ovos-skill-fallback-unknown.openvoiceos.start (Asserted ✅)
  • ovos.skills.fallback.ping (Asserted ✅)
  • ovos.utterance.cancelled (Asserted ✅)
  • ovos.utterance.handle (Asserted ✅)
  • ovos.utterance.handled (Asserted ✅)
  • recognizer_loop:utterance (Asserted ✅)
  • test_activate (Asserted ✅)

ovos-skill-count.openvoiceos

⚠️ Uncovered Listeners:

  • question:action (Intent)
  • question:action.ovos-skill-count.openvoiceos (Intent)
  • question:query (Intent)
  • homescreen.metadata.get
  • mycroft.ovos-skill-count.openvoiceos.all_loaded
  • mycroft.ovos-skill-count.openvoiceos.is_alive
  • mycroft.ovos-skill-count.openvoiceos.is_ready
  • mycroft.skill.disable_intent
  • mycroft.skill.enable_intent
  • mycroft.skill.remove_cross_context
  • mycroft.skill.set_cross_context
  • mycroft.skills.settings.changed
  • ovos-skill-count.openvoiceos.converse.get_response
  • ovos-skill-count.openvoiceos.set
  • ovos.common_query.ping
  • ovos.skills.settings_changed
    ✅ Covered Listeners:
  • mycroft.stop (4x)
  • ovos-skill-count.openvoiceos.stop (4x)
  • ovos-skill-count.openvoiceos.stop.ping (4x)
  • ovos-skill-count.openvoiceos:count_to_N.intent (4x)

ovos-skill-fallback-unknown.openvoiceos

⚠️ Uncovered Listeners:

  • question:action (Intent)
  • question:action.ovos-skill-fallback-unknown.openvoiceos (Intent)
  • question:query (Intent)
  • homescreen.metadata.get
  • mycroft.ovos-skill-fallback-unknown.openvoiceos.all_loaded
  • mycroft.ovos-skill-fallback-unknown.openvoiceos.is_alive
  • mycroft.ovos-skill-fallback-unknown.openvoiceos.is_ready
  • mycroft.skill.disable_intent
  • mycroft.skill.enable_intent
  • mycroft.skill.remove_cross_context
  • mycroft.skill.set_cross_context
  • mycroft.skills.settings.changed
  • ovos-skill-fallback-unknown.openvoiceos.converse.get_response
  • ovos-skill-fallback-unknown.openvoiceos.set
  • ovos-skill-fallback-unknown.openvoiceos.stop
  • ovos-skill-fallback-unknown.openvoiceos.stop.ping
  • ovos.common_query.ping
  • ovos.skills.settings_changed
    ✅ Covered Listeners:
  • mycroft.stop (4x)
  • ovos.skills.fallback.ovos-skill-fallback-unknown.openvoiceos.request (41x)
  • ovos.skills.fallback.ping (41x)

📤 Emitters:

  • ovos.skills.fallback.pong (Asserted ✅)
  • ovos.utterance.speak (Asserted ✅)

ovos-skill-hello-world.openvoiceos

⚠️ Uncovered Listeners:

  • ovos-skill-hello-world.openvoiceos:Greetings.intent (Intent)
  • ovos-skill-hello-world.openvoiceos:HelloWorldIntent (Intent)
  • ovos-skill-hello-world.openvoiceos:HowAreYou.intent (Intent)
  • ovos-skill-hello-world.openvoiceos:ThankYouIntent (Intent)
  • question:action (Intent)
  • question:action.ovos-skill-hello-world.openvoiceos (Intent)
  • question:query (Intent)
  • hello.world
  • homescreen.metadata.get
  • mycroft.ovos-skill-hello-world.openvoiceos.all_loaded
  • mycroft.ovos-skill-hello-world.openvoiceos.is_alive
  • mycroft.ovos-skill-hello-world.openvoiceos.is_ready
  • mycroft.skill.disable_intent
  • mycroft.skill.enable_intent
  • mycroft.skill.remove_cross_context
  • mycroft.skill.set_cross_context
  • mycroft.skills.settings.changed
  • ovos-skill-hello-world.openvoiceos.converse.get_response
  • ovos-skill-hello-world.openvoiceos.set
  • ovos-skill-hello-world.openvoiceos.stop
  • ovos-skill-hello-world.openvoiceos.stop.ping
  • ovos.common_query.ping
  • ovos.skills.settings_changed
    ✅ Covered Listeners:
  • mycroft.stop (4x)

stop.openvoiceos

⚠️ Uncovered Listeners:

  • question:action (Intent)
  • question:action.stop.openvoiceos (Intent)
  • question:query (Intent)
  • homescreen.metadata.get
  • mycroft.skill.disable_intent
  • mycroft.skill.enable_intent
  • mycroft.skill.remove_cross_context
  • mycroft.skill.set_cross_context
  • mycroft.skills.settings.changed
  • mycroft.stop.openvoiceos.all_loaded
  • mycroft.stop.openvoiceos.is_alive
  • mycroft.stop.openvoiceos.is_ready
  • ovos.common_query.ping
  • ovos.skills.settings_changed
  • stop.openvoiceos.converse.get_response
  • stop.openvoiceos.set
  • stop.openvoiceos.stop
  • stop.openvoiceos.stop.ping
    ✅ Covered Listeners:
  • mycroft.stop (4x)
  • stop:global (4x)
  • stop:skill (4x)

test_activation.openvoiceos

⚠️ Uncovered Listeners:

  • question:action (Intent)
  • question:action.test_activation.openvoiceos (Intent)
  • question:query (Intent)
  • homescreen.metadata.get
  • intent.service.skills.deactivated
  • mycroft.skill.disable_intent
  • mycroft.skill.enable_intent
  • mycroft.skill.remove_cross_context
  • mycroft.skill.set_cross_context
  • mycroft.skills.settings.changed
  • mycroft.test_activation.openvoiceos.all_loaded
  • mycroft.test_activation.openvoiceos.is_alive
  • mycroft.test_activation.openvoiceos.is_ready
  • ovos.common_query.ping
  • ovos.skills.settings_changed
  • test_activation.openvoiceos.converse.get_response
  • test_activation.openvoiceos.converse.ping
  • test_activation.openvoiceos.converse.request
  • test_activation.openvoiceos.deactivate
  • test_activation.openvoiceos.set
  • test_activation.openvoiceos.stop
  • test_activation.openvoiceos.stop.ping
  • test_deactivate
    ✅ Covered Listeners:
  • intent.service.skills.activated (5x)
  • mycroft.stop (4x)
  • test_activate (5x)
  • test_activation.openvoiceos.activate (5x)

📤 Emitters:

  • intent.service.skills.activate (Asserted ✅)
  • intent.service.skills.activated (Asserted ✅)
  • test_activation.openvoiceos.activate (Asserted ✅)

type

⚠️ Uncovered Listeners:

  • recognizer_loop:audio_output_start (Intent)
  • recognizer_loop:record_begin (Intent)
  • recognizer_loop:record_end (Intent)
    ✅ Covered Listeners:
  • recognizer_loop:audio_output_end (47x)


Built by scripts, maintained by community 🤝

JarbasAl and others added 3 commits June 29, 2026 16:19
Same pattern as StopService — handle_converse is called via bus event
dispatch after the converse pipeline stage matches, but had no lifecycle
signalling. Without it the dispatcher's in-flight entry for converse
dispatches would only ever resolve on timeout (10s).

Co-Authored-By: Claude
test_handle_global_stop_emits_mycroft_stop: check for
mycroft.skill.handler.start/complete instead of ovos.utterance.handled.

test_handle_skill_stop_forwards_to_skill: HandlerLifecycle emits
3 messages (start, forward, complete), not 1.

Co-Authored-By: Claude
Added assertions to check for 'mycroft.stop' and 'ovos.utterance.handled' messages in stop service tests.
@JarbasAl JarbasAl changed the base branch from dev to feat/pipeline1-handler-trio June 29, 2026 15:48
@JarbasAl JarbasAl marked this pull request as ready for review June 29, 2026 15:49
@JarbasAl JarbasAl merged commit 3694ded into feat/pipeline1-handler-trio Jun 29, 2026
12 of 19 checks passed
@JarbasAl JarbasAl deleted the feat/stop-handler-lifecycle branch June 29, 2026 15:49
JarbasAl added a commit that referenced this pull request Jun 29, 2026
StopService wraps its global/skill stop handlers in HandlerLifecycle (#796), so
the legacy mycroft.skill.handler.{start,complete} done-signal now brackets
mycroft.stop / {skill_id}.stop. Update the stop expectations to assert the trio.

The ping-pong tests built the stop message from a stale, test-local Session that
never saw the count skill's server-side self-activation (the count message,
serialized before activation, folds an empty active_skills back into the
singleton — correct SESSION-1 last-write-wins). Resend the live singleton session
for the stop turn, as a real client tracking responses would, instead of manually
activating — so the running skill is in active_skills and the ping-pong path runs
without a manual activate crutch. Also drop the §8 ovos.intent.handler.complete
from the expected lists where it is filtered via ignore_messages.

Co-Authored-By: Claude Opus 4.8 <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