Skip to content

Release 8.2.0a1#407

Open
github-actions[bot] wants to merge 50 commits into
masterfrom
release-8.2.0a1
Open

Release 8.2.0a1#407
github-actions[bot] wants to merge 50 commits into
masterfrom
release-8.2.0a1

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot commented Apr 9, 2026

Human review requested!

github-actions Bot and others added 30 commits November 10, 2025 02:04
fix: standalone skills wait_for_core
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* Add French workshop locale resources

* Refine French workshop dialogs

* Polish French workshop voice prompts
* fix: standalone skills wait_for_core

* fix: standalone skills wait_for_core

* fix: standalone skills wait_for_core

* fix: repair 6 failing tests and raise coverage from 45% to 54%

Test fixes:
- test_decorators.py: update TestKillableIntents for renamed class
  (KillableSkill → TestAbortSkill) and language-agnostic speak assertions
- test_decorators.py: fix test_get_response — abort_question must carry
  matching session_id; the killable_event session check silently ignores
  messages from a different session (root cause of the flaky failure)
- test_decorators.py: fix activate_msg type (intent.service.skills.activate
  → skill.converse.get_response.enable) per current ovos-workshop behaviour
- test_abstract_app.py: fix test_gui_init — GUIInterface.__len__==0 makes
  an empty gui falsy, so OVOSSkill._startup creates a new SkillGUI; assert
  type rather than identity
- skills/test_base.py: fix test_default_shutdown — change `if self.gui:` to
  `if self.gui is not None:` in OVOSSkill.default_shutdown so shutdown is
  always called on the SkillGUI regardless of its data contents

New tests (12 files, 170 new test cases):
- test_version.py, test_settings.py, test_permissions_extended.py
- test_backwards_compat.py, test_intents_extended.py
- test_decorators_layers_extended.py, test_resource_files_extended.py
- test_simple_imports.py (passive, layers, fallback_handler)
- skills/test_converse_extended.py, skills/test_auto_translatable_extended.py
- skills/test_common_play_extended.py, skills/test_game_skill_extended.py
- skills/test_intent_provider.py

Documentation (new + improved):
- docs/app.md, docs/game-skill.md, docs/auto-translatable.md
- docs/skill-api.md, docs/filesystem.md
- Improved docs/decorators.md (line citations, abort flow, OCP section)
- Improved docs/index.md (full class hierarchy, navigation table)
- Improved docs/skill-classes.md (added game, universal, active classes)
- FAQ.md, QUICK_FACTS.md, MAINTENANCE_REPORT.md, AUDIT.md, SUGGESTIONS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: audit — critical signature bug, bare excepts, missing type annotations

CRITICAL fix (common_play.py:466,481,492,501):
  __handle_ocp_pause/resume/next/prev each inspected signature(self.__playback_handler)
  to decide whether to pass `message=` to the OTHER handler. Replace with the
  correct handler's own signature in each case.

Bare except → explicit (5 locations):
  ovos.py:966   _cq_handler call
  ovos.py:1216  intent_file.build() — now logs warning
  ovos.py:1450  intent_parser.build() — now logs warning
  common_query_skill.py:177 CQS_match_query_phrase
  resource_files.py:89 language-tag distance computation

Type annotations added (ovos.py):
  load_regex_files, find_resource, _handle_first_run, _check_for_first_run,
  on_ready/error/stopping/alive/started_status, _handle_settings_changed,
  __handle_get_response, _handle_killed_wait_response

Type annotations added (fallback.py):
  register_fallback: callable -> Callable, added -> None, added Callable import

AUDIT.md updated with all findings (fixed + open), including:
  - race condition on __responses dict
  - busy-wait sleep loops in get_response
  - god class note (OVOSSkill 2500 lines)
  - magic numbers and variable shadowing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* build: migrate to pyproject.toml, add ovos-gui-api-client dependency

- Add pyproject.toml (setuptools backend) replacing setup.py
- Delete setup.py and MANIFEST.in (superseded by pyproject.toml)
- Add ovos-gui-api-client>=0.1.0,<1.0.0 to requirements.txt
- Update app.py import: ovos_bus_client.apis.gui → ovos_gui_api_client
- Add __version__ string to version.py derived from VERSION_* constants

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* deprecate: IdleDisplaySkill and resting_screen_handler decorator

The homescreen / idle display is now managed by the shell and its adapter
plugin (ovos-shell).  Skills must no longer subclass IdleDisplaySkill or
register with the homescreen manager directly.

Changes:
- idle_display_skill.py: replace full implementation with a deprecation stub
  that logs a warning on instantiation; retains the class name for import
  compatibility
- skills/__init__.py: remove IdleDisplaySkill from the public API exports
- decorators/__init__.py: remove resting_screen_handler decorator (replaced
  by the @homescreen_app decorator on OVOSSkill)
- test_idle_display_skill.py: update test to only assert the stub exists
- test_skill.py: remove broken test_stop test (tracked in AUDIT.md — Mock
  is not JSON-serialisable through FakeBus)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: migrate workflows to OpenVoiceOS/gh-automations@dev

- coverage.yml: update actions/checkout and actions/setup-python to v4/v5
- downstream.yml: replace bespoke pipdeptree script with reusable
  gh-automations downstream-check.yml workflow
- license_tests.yml: switch from neongeckocom to gh-automations
  license-check.yml; add dev branch PR trigger
- publish_stable.yml: migrate from TigreGotico gh-automations to
  OpenVoiceOS gh-automations; remove inline build/publish steps now
  handled by the reusable workflow; add sync_dev and publish_pypi flags
- release_workflow.yml: migrate from TigreGotico; collapse ~90 lines of
  inline steps into the reusable publish-alpha.yml call; add
  workflow_dispatch trigger; enable propose_release and notify_matrix
- unit_tests.yml: delete (replaced by build_tests.yml which uses the
  standardised gh-automations build-tests.yml reusable workflow)
- build_tests.yml: new workflow using gh-automations build-tests.yml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: revert accidental GUI client refactor and pyproject.toml migration

* update

* update

* update

* update

* update

* fix: catch AbortEvent in killable thread to prevent unhandled thread exception

pytest ≥3.11 ships a threadexception plugin that treats any unhandled
exception raised from a daemon thread as a test failure.  When
killable_event kills a running intent thread via raise_exc(AbortEvent),
the exception propagates up through the handler and exits the thread
without being caught, causing non-deterministic pytest failures.

Wrap the decorated function in a _guarded closure that catches AbortEvent
so the thread exits cleanly — it is an intentional kill, not an error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: narrow except clause and improve error logging

- resource_files.py: narrow bare `except Exception` to `except ValueError`
  in locate_lang_directories; tag_distance only raises ValueError for invalid
  language codes — catching everything masks unrelated errors
- skills/ovos.py: include exception instance in initialization failure log
  so the root cause is visible in traces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: address CodeRabbit review comments in unit tests

- test_abstract_app.py: fix constructor typo __int__ → __init__ (critical;
  subclass constructor was never called)
- test_idle_display_skill.py: replace misleading hasattr check with
  assertion that handle_idle is abstract
- test_converse_extended.py: tighten assertIsInstance(dict) to assertEqual({})
  for converse_matchers initialization
- test_common_play_extended.py: save/restore XDG env vars in tearDown;
  tighten ocp_cache_dir assertion using os.path.basename; add initial_count+1
  assertion to test_register_media_type
- test_decorators_layers_extended.py: add assertFalse(is_active) assertion
  after activate_layer on non-existent layer
- test_intent_provider.py: add actual DeprecationWarning assertion using
  warnings.catch_warnings (removes module from cache first so warning fires)
- test_decorators.py: remove unnecessary f-prefix from string literals with
  no interpolation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: fix existing workflows and add missing standard gh-automations workflows

Fixes:
- license_tests.yml: remove invalid empty with: block; add secrets: inherit
- build_tests.yml: add dev to push trigger branches (post-merge CI now runs)

Add missing standard reusable workflows (all using @dev ref):
- test.yml: unit tests on every PR/push via build-tests.yml
- lint.yml: ruff linting via lint.yml
- pip_audit.yml: CVE dependency scan via pip-audit.yml
- repo_health.yml: repo hygiene checks via repo-health.yml

All workflows reference OpenVoiceOS/gh-automations@dev per workspace policy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: fix CodeRabbit review comments in docs and FAQ

- docs/game-skill.md: add 'text' language specifier to fenced code block
- FAQ.md: fix path examples to use '.' instead of 'ovos-workshop/' prefix
- MAINTENANCE_REPORT.md: create required maintenance log (first entry)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Rename all locale directories to use canonical BCP-47 codes:
- Lowercase regions → uppercase (da-dk → da-DK, en-us → en-US)
- Bare language codes → full BCP-47 (da → da-DK, eu → eu-ES)
- Invalid codes remapped (eu-eu → eu-ES, fa-fa → fa-IR)
- Nested bare-language subdirs merged up (eu-ES/eu/ → eu-ES/)

This fixes wheel build failures caused by ZIP files containing
duplicate entries with different contents for the same logical path.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
JarbasAl and others added 20 commits April 3, 2026 16:43
list.pop() takes an integer index; passing a skill ID string raises
TypeError at runtime. list.remove() takes the value to remove.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: update locale lookups and tests after BCP-47 folder rename

The recent rename of locale folders from short codes (en-us, it, es) to
canonical BCP-47 tags (en-US, it-IT, es-ES) broke two things:

1. _get_word() stripped the region tag and looked for locale/it/ which
   no longer exists. Fix: try the full tag first, then the short code,
   then scan for any folder matching the language prefix.

2. test_base.py built expected file paths using the old lowercase folder
   names (en-us, uk-ua). Fix: update to en-US / uk-UA to match the
   renamed directories.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: use get_language_dir() from ovos-utils for all locale lookups

Replace ad-hoc lang string splitting and manual locale path construction
with ovos_utils.lang.get_language_dir(), which uses langcodes.tag_distance()
to find the best matching folder regardless of casing or regional variants.

- _get_dialog(): no longer strips region tag before building path
- _get_word(): replaces multi-step fallback scan with single get_language_dir() call
- CommonQuerySkill.__init__(): uses get_language_dir() instead of lang.split("-")[0]
- CommonQuerySkill translated_noise_words property/setter: use full lang tag as dict key

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add locale lookup and language resource tests

Validates the refactored get_language_dir()-based lookups:

- TestGetWord: canonical BCP-47, short code ('it', 'es', 'en'),
  lowercase tags ('en-us'), missing lang fallback, and a structural
  check that every locale folder's word_connectors.json has 'and'/'or'
- TestGetDialog: resolves bundled .dialog files by canonical, lowercase,
  and short-code lang tags; verifies context rendering; verifies fallback
  for missing file and unknown lang
- TestJoinWordList: end-to-end for English, Italian (euphony e→ed, o→od),
  Spanish (euphony y→e, o→u), German, French; also single-item and
  empty-list edge cases; short-code equivalence checks

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor: route all workshop locale lookups through CoreResources

_get_dialog() and _get_word() were still building paths manually even
after the get_language_dir() step. CoreResources / SkillResources in
resource_files.py already handle the full lookup chain (locale/,
dialog/, vocab/ subdirectories, user overrides, tag distance matching)
— there is no reason to duplicate that logic.

- _get_dialog(): delegate to CoreResources(lang).load_dialog_file()
- _get_word(): delegate to CoreResources(lang).load_json_file()
- CommonQuerySkill.__init__: delegate to CoreResources(lang).load_list_file()
- Remove manual path construction, os.listdir scans, resolve_resource_file
  calls and the get_language_dir import that were introduced in earlier
  steps — resource_files.py is the canonical place for this

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: use self.resources inside skill class instead of calling _get_dialog

ResourceFile._locate() already falls back to workshop_directory (the
matching lang dir in ovos_workshop/locale/) so self.resources will find
both skill-local AND workshop-bundled files. There is no reason for code
inside the skill class to call the module-level _get_dialog() helper —
that function is only needed outside a skill instance context (e.g. in
join_word_list which has no self).

_get_dialog / _get_word / CoreResources are still the right tool for
module-level utilities. Inside the skill, self.resources is canonical.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove redundant CoreResources fallback in voc_list

self.resources already searches workshop_directory (ovos_workshop/locale/{lang}/)
as a fallback in ResourceFile._locate(), so the explicit CoreResources(lang)
fallback was redundant. CoreResources now only appears in module-level
helpers (_get_dialog, _get_word) that have no skill instance.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: remove unused method

* chore: remove unused method

* fix: use self.resources in CommonQuerySkill instead of CoreResources

self.resources already searches workshop_directory as a fallback, so
CoreResources was redundant here too. CoreResources is now only used in
module-level free functions (_get_dialog, _get_word) that have no self.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: replace _get_dialog calls in game_skill with speak_dialog

_get_dialog no longer exists as a public symbol; game_skill.py was
importing it. speak_dialog(key) uses self.resources (which already
falls back to workshop_directory) and is the correct API for skills.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: align remove_noise() lang key with _translated_noise_words cache

Cache is written with the full BCP-47 tag (e.g. "en-US") but
remove_noise() stripped to the base subtag ("en") before lookup,
so the dict lookup always missed. Use the full tag consistently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: remove deprecation flagged for 0.1.0

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore: remove deprecated class

* chore: remove deprecated class
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
* refactor: use JSON-based euphony rules for word list joining

Extracted word joining logic into ovos_workshop/skills/util.py with
generic, language-configurable euphony rules loaded from JSON:

- join_word_list() now supports per-language euphony.json config files
- Italian and Spanish euphony rules defined in locale/{lang}/euphony.json
- Removed hardcoded _join_word_list_it/es() special case handlers
- Rules engine supports: starts_with_vowel, starts_with_letter,
  starts_with_any_except conditions with accent normalization
- Moved simple_trace() to util.py for cleaner ovos.py module

This enables language teams to contribute euphony rules without modifying
Python code, improving i18n scalability for ask_selection() formatting.

All existing tests pass; word joining behavior is identical.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* mark functions as private, not meant to be imported by downstream

* feat: add word_connectors.json for 26 new languages and comprehensive tests

Added word connectors (and/or translations) for: ar-SA, bg-BG, el-GR,
et-EE, eu-ES, fi-FI, he-IL, hi-IN, hr-HR, hu-HU, id-ID, ja-JP, ko-KR,
lt-LT, lv-LV, ms-MY, nb-NO, pt-BR, ro-RO, ru-RU, sk-SK, sv-SE, sw-KE,
th-TH, tr-TR, vi-VN, zh-CN.

Tests added:
- simple_trace() formatting
- _normalize_word() accent/h-stripping
- _apply_euphony() rule engine (all condition types)
- euphony.json schema validation for all locales
- word_connectors.json presence and schema for all locales
- join_word_list() for 9 new languages (ru, tr, ja, zh, ar, sv, hu, pt-BR)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* remove unused files

* feat: add Occitan, Asturian, and Aragonese locale support with euphony

- oc-FR: "e" → "et" before any vowel
- ast-ES: "y" → "e" before /i/ (Spanish-like), "o" → "u" before /o/
- an-ES: "y" → "e" before /i/ (Spanish-like), "o" → "u" before /o/

All three are the remaining languages with productive conjunction euphony.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: update test_euphony.py to use new join_word_list API

The old test imported removed internal functions (_join_word_list_it,
_join_word_list_es). Updated to use join_word_list from util.py with
the lang parameter, preserving all original test cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* 📝 Add docstrings to `feat/word-join-euphony-json`

Docstrings generation was requested by @JarbasAl.

The following files were modified:

* `ovos_workshop/skills/ovos.py`
* `ovos_workshop/skills/util.py`

These files were kept as they were:
* `test/unittests/test_euphony.py`
* `test/unittests/test_locale_lookup.py`

These file types are not supported:
* `ovos_workshop/locale/an-ES/euphony.json`
* `ovos_workshop/locale/an-ES/word_connectors.json`
* `ovos_workshop/locale/ar-SA/word_connectors.json`
* `ovos_workshop/locale/ast-ES/euphony.json`
* `ovos_workshop/locale/ast-ES/word_connectors.json`
* `ovos_workshop/locale/bg-BG/word_connectors.json`
* `ovos_workshop/locale/el-GR/word_connectors.json`
* `ovos_workshop/locale/es-ES/euphony.json`
* `ovos_workshop/locale/et-EE/word_connectors.json`
* `ovos_workshop/locale/eu-ES/word_connectors.json`
* `ovos_workshop/locale/fi-FI/word_connectors.json`
* `ovos_workshop/locale/he-IL/word_connectors.json`
* `ovos_workshop/locale/hi-IN/word_connectors.json`
* `ovos_workshop/locale/hr-HR/word_connectors.json`
* `ovos_workshop/locale/hu-HU/word_connectors.json`
* `ovos_workshop/locale/id-ID/word_connectors.json`
* `ovos_workshop/locale/it-IT/euphony.json`
* `ovos_workshop/locale/ja-JP/word_connectors.json`
* `ovos_workshop/locale/ko-KR/word_connectors.json`
* `ovos_workshop/locale/lt-LT/word_connectors.json`
* `ovos_workshop/locale/lv-LV/word_connectors.json`
* `ovos_workshop/locale/ms-MY/word_connectors.json`
* `ovos_workshop/locale/nb-NO/word_connectors.json`
* `ovos_workshop/locale/oc-FR/euphony.json`
* `ovos_workshop/locale/oc-FR/word_connectors.json`
* `ovos_workshop/locale/pt-BR/word_connectors.json`
* `ovos_workshop/locale/ro-RO/word_connectors.json`
* `ovos_workshop/locale/ru-RU/word_connectors.json`
* `ovos_workshop/locale/sk-SK/word_connectors.json`
* `ovos_workshop/locale/sv-SE/word_connectors.json`
* `ovos_workshop/locale/sw-KE/word_connectors.json`
* `ovos_workshop/locale/th-TH/word_connectors.json`
* `ovos_workshop/locale/tr-TR/word_connectors.json`
* `ovos_workshop/locale/vi-VN/word_connectors.json`
* `ovos_workshop/locale/zh-CN/word_connectors.json`

* fix: apply CodeRabbit auto-fixes

Fixed 2 file(s) based on 2 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
* feat: yesno/selection agent plugins

* fix: default plugins

* fix: dependencies

* chore: drop requirements.txt, deps now in pyproject.toml

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: correct test patch paths and expected fallback types

- patch load_yesno_plugin/load_option_matcher_plugin at ovos_workshop.skills.ovos
  (not ovos_plugin_manager.agents) since they are imported into the module
- _get_yesno_engine falls back to HeuristicYesNoEngine, not None
- _get_selection_engine falls back to FuzzyOptionMatcherPlugin, not None
- replace YesNoSolver references with patch.object on _get_yesno_engine

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: drop duplicate unit_tests workflow

build_tests already runs the same test path; unit_tests only differed by
adding audio system deps not needed for these tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add ask_yesno and ask_selection tests

- test_base.py: fill in TODO stubs with mock-based tests on real OVOSSkill
  (yes/no/unmatched/timeout for ask_yesno; empty/single/invalid/fuzzy/timeout
  for ask_selection)
- test_ask_e2e.py: ovoscope end-to-end tests — inline AskYesNoSkill and
  AskSelectionSkill loaded via get_minicroft; user response injected via
  {skill_id}.converse.get_response after hearing speak; skipped when
  ovoscope/ovos-core are not installed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: require ovoscope for e2e tests, add [test] extras

- remove pytest.importorskip — e2e tests are mandatory, not optional
- add [project.optional-dependencies] test group to pyproject.toml
  (ovos-core, ovoscope, pytest, pytest-cov, ovos-translate-server-plugin)
- build_tests.yml: pass install_extras=test so CI installs test deps
- keep requirements/test.txt in sync for local pip install -r workflows

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: remove unsupported message kwarg from ask_yesno/ask_selection calls

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* .

* fix: forward min_conf to OptionMatcher engine via config dict

The ask_selection min_conf parameter was documented but silently ignored.
FuzzyOptionMatcherPlugin reads threshold from self.config["min_conf"],
so set it on the cached engine instance before each match_option call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: sync documentation after yesno/selection plugin integration

AI-Generated Change:
- Model: claude-sonnet-4-6
- Intent: keep docs accurate after feat: yesno/selection agent plugins
- Impact: updated README.md, docs/index.md, docs/ovos-skill.md, docs/skill-interaction.md
- Changes:
  - README.md: rewritten with install instructions, config table, and docs links
  - docs/skill-interaction.md: fixed fallback class name (HeuristicYesNoEngine not YesNoSolver), corrected ask_yesno_plugin default (ovos-solver-yes-no-plugin not None), added line-number citations, corrected failure behaviour table
  - docs/ovos-skill.md: noted plugin configurability for ask_yesno/ask_selection
  - docs/index.md: added skill-interaction.md to navigation table
- Verified via: manual review against ovos_workshop/skills/ovos.py:1932-2040

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: move ovoscope e2e tests out of build_tests path to prevent hangs

get_minicroft blocks up to 60s waiting for READY — too heavy for the
standard build_tests matrix. Move test/unittests/test_ask_e2e.py to
test/ovoscope/ (not scanned by build_tests) and add a dedicated
ovoscope_tests workflow that installs [test] extras and runs that path.
Revert install_extras from build_tests.yml.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: unblock speak(wait=True) in e2e tests by emitting audio_output_end

ask_selection speaks each option with wait=True, blocking 15s per speak
waiting for recognizer_loop:audio_output_end that never arrives in tests.
Emit that event immediately on every speak message so the skill proceeds
without delay, then inject the user response once speaking settles.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Delete MAINTENANCE_REPORT.md

* fix: use bus.on("speak") not on("message") to avoid recursion in e2e tests

Listening on "message" caught the audio_output_end we emitted, causing
infinite recursion. Listen on "speak" specifically instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: merge ovoscope e2e tests back into build_tests

No need for a separate workflow. Move test_ask_e2e.py back to
test/unittests/ and install [test] extras in build_tests so ovoscope
and ovos-core are available.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: delay audio_output_end emission past sess.is_speaking=True

FakeBus is synchronous — on_speak fires inline during bus.emit(speak),
before the caller sets sess.is_speaking=True. Emitting audio_output_end
in the same stack means wait_while_speaking misses it and hangs 15s.
Use a daemon thread with a 20ms delay so is_speaking is set first.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: remove fragile ovoscope e2e tests for ask_yesno/ask_selection

The tests fought TTS wait machinery (speak(wait=True) blocks on
recognizer_loop:audio_output_end) without adding coverage beyond what
test_skill_interaction.py already provides via mocked get_response.
Unit tests are sufficient here.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: drop ovoscope from test deps, revert install_extras

No ovoscope e2e tests remain, no need for the heavy dependency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: restore ovoscope e2e tests with proper TTS wait fix

Patch SessionManager.wait_while_speaking to a no-op so speak(wait=True)
doesn't block on TTS completion. Inject user response via
skill.converse.get_response.enable event instead of timing hacks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: restore SessionManager.bus after e2e tests to prevent state leak

get_minicroft sets SessionManager.bus globally. Without restoring it,
subsequent tests get real wait_while_speaking behaviour instead of the
early-return they expect (bus=None). Also scope wait_while_speaking
patch to setUp/tearDown so it can't leak if tearDownClass throws.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <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.

2 participants