Skip to content

fix: conform transformer chains to OVOS-TRANSFORM-1#785

Draft
JarbasAl wants to merge 2 commits into
devfrom
fix/transform-1-conformance
Draft

fix: conform transformer chains to OVOS-TRANSFORM-1#785
JarbasAl wants to merge 2 commits into
devfrom
fix/transform-1-conformance

Conversation

@JarbasAl

@JarbasAl JarbasAl commented Jun 27, 2026

Copy link
Copy Markdown
Member

Brings ovos-core into conformance with OVOS-TRANSFORM-1. Originally
reimplemented the utterance / metadata / intent transformer chains locally in
ovos_core/transformers.py; dev has since refactored those services to
subclass the shared TransformersService bases in ovos-plugin-manager, so
this PR was reworked to move the conformance logic there instead
(ovos-plugin-manager#417, see "Depends on" below) and keep only the
core-only pieces here:

What's in this PR

  • §8.2 ovos.utterance.cancelled payload. IntentService.send_cancel_event
    now surfaces cancel_reason / cancel_by (stamped by the §8.1 cancellation
    signal) on the emitted ovos.utterance.cancelled message, when present.
  • ovos-plugin-manager floor bump to >=2.10.3a1 — the next prerelease
    that will contain the shared-base conformance work.
  • Test coverage: test_intent_service_extended.py gains §8.2 coverage for
    cancel_reason/cancel_by propagation (and their absence).
    test_transformers.py's intent-transform test was adapted to the now
    shared-base-enforced §3.4 identity invariant — a Match that changes
    match_type is correctly rejected rather than silently passed through; a
    dedicated rejection test and an identity-preserving enrichment test were
    added.

What moved to ovos-plugin-manager#417

ovos_core/transformers.py stays as dev's thin subclasses of the shared
bases; §1.3 provenance stamping, §7 wrong-shape rejection, and the §3.4
intent dispatch-identity invariant now live in
ovos_plugin_manager/transformer_services.py so every consumer of the
shared bases gets them, not just ovos-core. Priority-ascending ordering,
the order config override, and §8.1 cancellation stamping were already
conformant in the shared bases.

Depends on ovos-plugin-manager#417 — must merge first (and this PR's
floor-pin bump land in a release) before this PR is testable against a
released ovos-plugin-manager.

Testing

  • Full test/unittests/ suite run locally with ovos-plugin-manager#417's
    branch installed editable: 304 passed, no regressions vs. clean dev.
  • The one failure surfaced by combining with ovos-plugin-manager#417
    (test_transform_returns_modified_intent asserting a raw match_type
    swap passes through) was a stale test exercising behavior the spec
    disallows; fixed as described above rather than worked around.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Cancellation events now include the cancellation reason and initiator when available.
    • Intent matching preserves enriched match details when dispatch identity remains valid.
  • Bug Fixes

    • Invalid changes to intent dispatch identity are now rejected, preserving the original matching behavior.

@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

send_cancel_event now forwards optional cancellation metadata. Transformer tests enforce dispatch identity preservation, and the minimum ovos-plugin-manager version is updated.

Changes

Cancellation metadata

Layer / File(s) Summary
Cancellation event payload
ovos_core/intent_services/service.py, test/unittests/test_intent_service_extended.py
Cancellation messages include cancel_reason and cancel_by only when supplied in message context, with tests covering both cases.

Transformer dispatch identity

Layer / File(s) Summary
Transformer contract validation
pyproject.toml, test/unittests/test_transformers.py
The minimum ovos-plugin-manager version is raised, and tests verify enriched match data is retained while changed dispatch identity is rejected.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

🚥 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 clearly matches the PR's main purpose: OVOS-TRANSFORM-1 conformance for transformer chains and related intent-service/test updates.
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.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/transform-1-conformance

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 added the fix label Jun 27, 2026
@github-actions

github-actions Bot commented Jun 27, 2026

Copy link
Copy Markdown

Beep boop! Data processing finished. ⚙️

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

📋 Repo Health

A quick checkup for the repository! 🩺

✅ All required files present.

Latest Version: 2.5.6a1

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

🏷️ Release Preview

The release tag is ready to be minted. 🏷️

Current: 2.5.6a1Next: 2.5.7a1

Signal Value
Label fix
PR title fix: conform transformer chains to OVOS-TRANSFORM-1
Bump build

✅ PR title follows conventional commit format.


🚀 Release Channel Compatibility

Predicted next version: 2.5.7a1

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

🌍 Locale Build

Everything looks good so far! ✅

✅ 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 (fr-fr, ca-es, nl-be, es-es, eu-ES...)

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

  • intent_services/locale/*/*.voc

Build manifest: ✅ 31 locale files included in package

🔒 Security (pip-audit)

Ensuring our security logs are being captured. 🪵

✅ No known vulnerabilities found (110 packages scanned).

📊 Coverage

The coverage audit is ready for your inspection. 📋

⚠️ 64.0% total coverage

Files below 80% coverage (9 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 49.9% 186
ovos_core/skill_manager.py 58.8% 173
ovos_core/transformers.py 70.5% 52
ovos_core/intent_services/dispatcher.py 73.4% 29

Full report: download the coverage-report artifact.

🔨 Build Tests

Ensuring the foundation is solid for these changes. 🏛️

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

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

⚖️ License Check

Ensuring our project remains legally compliant. ✅

✅ No license violations found.

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

🔌 Skill Tests (ovoscope)

Testing the skill's 'long-term memory' (aka settings/state). 🧠

2/36 passed

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

test_intent_blacklist failure:

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

test_padatious_no_match failure:

[gw3] 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:

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

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 ❌ subtests passed
test_count_infinity_global ❌ subtests passed
test_count_infinity_active ❌ subtests passed
test_count_infinity_stop_low ❌ subtests passed

test_count failure:

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

test_count_infinity_global failure:

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

test_count_infinity_active failure:

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

test_deactivate_inside_converse failure:

[gw1] 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:

[gw1] 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 ❌ subtests passed

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_stop_voc_exact_still_works ❌ subtests passed
test_global_stop_voc_no_active_skills ❌ subtests passed

test_stop_voc_exact_still_works failure:

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

test_global_stop_voc_no_active_skills failure:

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

test_blacklisted_skill_falls_through_to_failure failure:

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

test_no_match_produces_intent_failure failure:

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

test_high_priority_stage_handles_before_low failure:

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

test_lang_text_detection failure:

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

test_metadata_preferred_over_text_detection failure:

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

test_invalid_lang_detection failure:

[gw1] 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:

[gw0] 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_adapt_no_match ❌ subtests passed
test_skill_blacklist ❌ subtests passed
test_padatious_match ❌ subtests passed

test_intent_blacklist failure:

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

test_adapt_no_match failure:

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

test_skill_blacklist 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 ❌ subtests passed
test_exact ❌ subtests passed

test_not_exact_high failure:

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

test_not_exact_med failure:

[gw0] 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
❌ **TestStopServiceNotASkill** — 0/1
Test Result
test_stop_service_is_not_a_skill ❌ subtests passed

test_stop_service_is_not_a_skill failure:

[gw2] 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 ❌ subtests passed

test_stop_with_active_skill_ping_pong failure:

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

🚌 Bus Coverage

Ensuring every intent and event is reached by the test suite. 🎯

🔴 Coverage Summary

Metric Status Coverage
Listeners ██░░░░░░░░ 22.6% 49/217 handlers
Emitters ██████████ 100% 35/35 observed
Assertions ██████████ 100% 35/35 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 0/2 (0.0%) 0/0 0/0
HierarchicalAdaptPipeline 4/14 (28.6%) 0/0 0/0
IntentDispatcher 1/2 (50.0%) 0/0 0/0
IntentManifest 0/8 (0.0%) 0/0 0/0
IntentService 3/4 (75.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
StopService 2/2 (100.0%) 0/0 0/0
__core__ 5/24 (20.8%) 6/6 6/6
ovos-skill-count.openvoiceos 4/20 (20.0%) 8/8 8/8
ovos-skill-hello-world.openvoiceos 3/24 (12.5%) 10/10 10/10
stop.openvoiceos 0/0 (0.0%) 8/8 8/8
test_activation.openvoiceos 4/27 (14.8%) 3/3 3/3
type 2/4 (50.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 (142x)
  • intent.service.adapt.manifest.get (594x)
  • register_intent (130x)
  • register_vocab (728x)

ConverseService

⚠️ Uncovered Listeners:

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

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 (142x)
  • intent.service.adapt.manifest.get (594x)
  • register_intent (130x)
  • register_vocab (728x)

FallbackService

⚠️ Uncovered Listeners:

  • ovos.skills.fallback.deregister
  • ovos.skills.fallback.register

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 (142x)
  • intent.service.adapt.manifest.get (594x)
  • register_intent (130x)
  • register_vocab (728x)

IntentDispatcher

⚠️ Uncovered Listeners:

  • mycroft.skill.handler.error
    ✅ Covered Listeners:
  • mycroft.skill.handler.complete (123x)

IntentManifest

⚠️ Uncovered Listeners:

  • ovos.intent.deregister (Intent)
  • ovos.intent.describe (Intent)
  • ovos.intent.disable (Intent)
  • ovos.intent.enable (Intent)
  • ovos.intent.list (Intent)
  • ovos.intent.register.keyword (Intent)
  • ovos.intent.register.template (Intent)
  • ovos.skill.deregister

IntentService

⚠️ Uncovered Listeners:

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

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 (142x)
  • padatious:register_intent (169x)
  • register_intent (130x)

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 (142x)
  • padatious:register_intent (169x)
  • register_intent (130x)

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 (142x)
  • 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 (142x)
  • intent.service.padatious.manifest.get (594x)
  • mycroft.skills.train (153x)
  • padatious:register_intent (169x)

SkillManager

⚠️ Uncovered Listeners:

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

StopService

✅ Covered Listeners:

  • stop:global (42x)
  • stop:skill (21x)

__core__

⚠️ Uncovered Listeners:

  • add_context
  • clear_context
  • message
  • mycroft.ovos-skill-count.openvoiceos.all_loaded
  • mycroft.ovos-skill-count.openvoiceos.is_alive
  • mycroft.ovos-skill-count.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-count.openvoiceos.set
  • ovos-skill-hello-world.openvoiceos.set
  • ovos.session.sync
  • remove_context
  • skill.converse.get_response.disable
  • skill.converse.get_response.enable
  • test_activation.openvoiceos.set
    ✅ Covered Listeners:
  • ovos-skill-count.openvoiceos.stop.response (20x)
  • ovos.session.update_default (153x)
  • ovos.utterance.handled (166x)
  • ovos.utterance.speak (74x)
  • skill.stop.pong (20x)

📤 Emitters:

  • mycroft.audio.play_sound (Asserted ✅)
  • ovos.intent.unmatched (Asserted ✅)
  • ovos.utterance.handle (Asserted ✅)
  • ovos.utterance.handled (Asserted ✅)
  • recognizer_loop:utterance (Asserted ✅)
  • test_deactivate (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 (39x)
  • ovos-skill-count.openvoiceos.stop (21x)
  • ovos-skill-count.openvoiceos.stop.ping (21x)
  • ovos-skill-count.openvoiceos:count_to_N.intent (28x)

📤 Emitters:

  • mycroft.skill.handler.complete (Asserted ✅)
  • mycroft.skill.handler.start (Asserted ✅)
  • ovos-skill-count.openvoiceos.activate (Asserted ✅)
  • ovos-skill-count.openvoiceos:count_to_N.intent (Asserted ✅)
  • ovos.intent.handler.complete (Asserted ✅)
  • ovos.intent.handler.start (Asserted ✅)
  • ovos.intent.matched (Asserted ✅)
  • ovos.utterance.handled (Asserted ✅)

ovos-skill-hello-world.openvoiceos

⚠️ Uncovered Listeners:

  • 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 (42x)
  • ovos-skill-hello-world.openvoiceos:Greetings.intent (3x)
  • ovos-skill-hello-world.openvoiceos:HelloWorldIntent (29x)

📤 Emitters:

  • mycroft.skill.handler.complete (Asserted ✅)
  • mycroft.skill.handler.start (Asserted ✅)
  • ovos-skill-hello-world.openvoiceos.activate (Asserted ✅)
  • ovos-skill-hello-world.openvoiceos:Greetings.intent (Asserted ✅)
  • ovos-skill-hello-world.openvoiceos:HelloWorldIntent (Asserted ✅)
  • ovos.intent.handler.complete (Asserted ✅)
  • ovos.intent.handler.start (Asserted ✅)
  • ovos.intent.matched (Asserted ✅)
  • ovos.utterance.handled (Asserted ✅)
  • ovos.utterance.speak (Asserted ✅)

stop.openvoiceos

📤 Emitters:

  • mycroft.skill.handler.complete (Asserted ✅)
  • mycroft.skill.handler.start (Asserted ✅)
  • mycroft.stop (Asserted ✅)
  • ovos-skill-count.openvoiceos.stop (Asserted ✅)
  • ovos.utterance.handled (Asserted ✅)
  • stop.openvoiceos.activate (Asserted ✅)
  • stop:global (Asserted ✅)
  • stop:skill (Asserted ✅)

test_activation.openvoiceos

⚠️ Uncovered Listeners:

  • question:action (Intent)
  • question:action.test_activation.openvoiceos (Intent)
  • question:query (Intent)
  • homescreen.metadata.get
  • intent.service.skills.activated
  • 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_activate
  • test_activation.openvoiceos.activate
  • test_activation.openvoiceos.converse.get_response
  • test_activation.openvoiceos.converse.ping
  • test_activation.openvoiceos.converse.request
  • test_activation.openvoiceos.set
  • test_activation.openvoiceos.stop
  • test_activation.openvoiceos.stop.ping
    ✅ Covered Listeners:
  • intent.service.skills.deactivated (7x)
  • mycroft.stop (28x)
  • test_activation.openvoiceos.deactivate (7x)
  • test_deactivate (7x)

📤 Emitters:

  • intent.service.skills.deactivate (Asserted ✅)
  • intent.service.skills.deactivated (Asserted ✅)
  • test_activation.openvoiceos.deactivate (Asserted ✅)

type

⚠️ Uncovered Listeners:

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

📚 Docs

I've finished the digital walk-through of your PR. 🚶‍♂️

✅ All required documentation files present.

README.md

🔎 Type Check

The automated report has been generated. 🖨️

mypy: 270 error(s) found

test/end2end/test_activate.py:9:1: error: Cannot find implementation or library stub for module named "ovoscope" [import-not-found]

Errors (showing first 10/270)
test/unittests/test_skill/__init__.py:15:1: error: Cannot find implementation or library stub for module named "ovos_workshop.skills.ovos"  [import-not-found]
test/end2end/conftest.py:10:1: error: Cannot find implementation or library stub for module named "pytest"  [import-not-found]
ovos_core/transformers.py:3:1: error: Cannot find implementation or library stub for module named "ovos_config"  [import-not-found]
ovos_core/transformers.py:4:1: error: Cannot find implementation or library stub for module named "ovos_plugin_manager.intent_transformers"  [import-not-found]
ovos_core/transformers.py:5:1: error: Cannot find implementation or library stub for module named "ovos_plugin_manager.metadata_transformers"  [import-not-found]
ovos_core/transformers.py:6:1: error: Cannot find implementation or library stub for module named "ovos_plugin_manager.text_transformers"  [import-not-found]
ovos_core/transformers.py:7:1: error: Cannot find implementation or library stub for module named "ovos_plugin_manager.transformer_services"  [import-not-found]
ovos_core/intent_services/manifest.py:15:1: error: Cannot find implementation or library stub for module named "ovos_bus_client.message"  [import-not-found]
ovos_core/intent_services/manifest.py:16:1: error: Cannot find implementation or library stub for module named "ovos_spec_tools"  [import-not-found]
ovos_core/intent_services/manifest.py:17:1: error: Cannot find implementation or library stub for module named "ovos_utils.log"  [import-not-found]

🔌 Plugin Detection

Checking if the plugin ecosystem recognizes this contribution... 🌐

ℹ️ Not an OVOS plugin — OPM check skipped.


Crafting a better voice assistant, one commit at a time 🎙️

@JarbasAl
JarbasAl force-pushed the fix/transform-1-conformance branch from 31dd04e to ed394c1 Compare July 3, 2026 23:28
@JarbasAl
JarbasAl marked this pull request as ready for review July 16, 2026 07:21
JarbasAl and others added 2 commits July 23, 2026 22:56
Bring the utterance / metadata / intent transformer chains into
conformance with the OVOS-TRANSFORM-1 specification:

- §4 ordering: the chain MUST run in ascending priority order (lower
  number = earlier). All three services sorted with reverse=True
  (descending); drop reverse so priority 1 runs before 50 runs before
  100, matching the spec and the fallback-skill convention.
- §1.3 provenance: stamp each touching transformer's id as the last
  element of the corresponding <type>_transformer_ids context list.
- §3.4 / §9 intent identity: reject an intent transformer return whose
  dispatch identity (match_type / skill_id) differs from its input,
  treating it as a §7 shape violation and keeping the prior Match.
- §8.1 cancel_by: when a transformer signals cancellation, the
  orchestrator stamps cancel_by from the emitting transformer's id
  (not from any payload value), stops the chain, and surfaces
  cancel_reason / cancel_by on ovos.utterance.cancelled (§8.2).
- §7 wrong-shape returns: reject wrong-shape transformer output
  (wrong type / arity, non-dict context, non-Match intent) instead of
  silently unpacking it; keep the prior output unchanged.

Extends test/unittests/test_transformers.py with conformance tests for
each MUST and flips the existing priority-ordering assertions from
descending to ascending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…VOS-TRANSFORM-1 conformance

dev's transformer chains already subclass ovos-plugin-manager's shared
TransformersService bases. This PR's OVOS-TRANSFORM-1 conformance work
moved into ovos-plugin-manager#417 (§1.3 provenance, §7 wrong-shape
rejection, §3.4 intent identity invariant); ovos_core/transformers.py
stays as dev's thin subclasses.

- Bump the ovos-plugin-manager floor to >=2.10.3a1 (next prerelease
  containing #417's conformance work).
- Adapt test_transformers.py's TestIntentTransformersServiceTransform
  to the now-enforced §3.4 identity invariant: a Match that changes
  match_type is correctly rejected rather than passed through; add a
  dedicated rejection test and an identity-preserving enrichment test.
- Add §8.2 coverage in test_intent_service_extended.py for
  ovos.utterance.cancelled surfacing cancel_reason/cancel_by from the
  §8.1 signal (core-only wiring, not duplicated by the shared bases).
@github-actions github-actions Bot added fix and removed fix labels Jul 23, 2026
@JarbasAl
JarbasAl force-pushed the fix/transform-1-conformance branch from ed394c1 to 7055e06 Compare July 23, 2026 21:59
@github-actions github-actions Bot added fix and removed fix labels Jul 23, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@test/unittests/test_transformers.py`:
- Around line 367-390: The dispatch-identity tests currently only exercise
match_type because both fixtures use skill_id=None. Update
test_transform_accepts_enriched_match and
test_transform_rejects_dispatch_identity_change to use a non-null original
skill_id, and add/assert a transformed match with a different skill_id is
rejected while the original match is retained.
🪄 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 Plus

Run ID: fa097521-65b8-408d-b036-64880c84f4ac

📥 Commits

Reviewing files that changed from the base of the PR and between 9fa005a and 7055e06.

📒 Files selected for processing (4)
  • ovos_core/intent_services/service.py
  • pyproject.toml
  • test/unittests/test_intent_service_extended.py
  • test/unittests/test_transformers.py

Comment on lines +367 to +390
original = _make_intent_match("original:intent")
modified = IntentHandlerMatch(
match_type="original:intent",
match_data={"enriched": True},
skill_id=original.skill_id,
utterance="hello",
)
p = _make_mock_plugin("p", priority=50)
p.transform.return_value = modified
svc = _make_intent_service(plugins=[p])
result = svc.transform(original)
self.assertEqual(result.match_data, {"enriched": True})

def test_transform_rejects_dispatch_identity_change(self):
"""OVOS-TRANSFORM-1 §3.4: a returned Match whose match_type/skill_id
differs from its input is a §7 shape violation -- discarded, prior
Match kept."""
original = _make_intent_match("original:intent")
modified = _make_intent_match("modified:intent")
p = _make_mock_plugin("p", priority=50)
p.transform.return_value = modified
svc = _make_intent_service(plugins=[p])
result = svc.transform(original)
self.assertEqual(result.match_type, "modified:intent")
self.assertEqual(result.match_type, "original:intent")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Cover skill_id in the dispatch-identity tests.

Both fixtures use skill_id=None, so these tests only validate match_type identity. Add a case with a non-null original skill and a transformed match whose skill_id changes, asserting that the original match is retained.

🤖 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 `@test/unittests/test_transformers.py` around lines 367 - 390, The
dispatch-identity tests currently only exercise match_type because both fixtures
use skill_id=None. Update test_transform_accepts_enriched_match and
test_transform_rejects_dispatch_identity_change to use a non-null original
skill_id, and add/assert a transformed match with a different skill_id is
rejected while the original match is retained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant