Skip to content

refactor: route OVOSSkill resource loading through ovos-spec-tools (back-compat)#414

Open
JarbasAl wants to merge 4 commits into
devfrom
feat/drop-resource-files-usage
Open

refactor: route OVOSSkill resource loading through ovos-spec-tools (back-compat)#414
JarbasAl wants to merge 4 commits into
devfrom
feat/drop-resource-files-usage

Conversation

@JarbasAl
Copy link
Copy Markdown
Member

@JarbasAl JarbasAl commented May 25, 2026

Supersedes #413 (closed during squash). Part of the OVOS migration onto ovos-spec-tools (architecture#7).

OVOSSkill and the rest of ovos_workshop now route resource access through ovos_spec_tools.LocaleResources directly. The legacy ovos_workshop.resource_files.SkillResources is no longer used internally but stays shipped and stays available on every OVOSSkill through a deprecation mixin — every skill in the ecosystem keeps working.

Back-compat surface preserved

All legacy entry points stay on OVOSSkill via the new _LegacyResourcesMixin. Each emits a single DeprecationWarning and routes through the same cached SkillResources instance so behaviour is unchanged.

Legacy member Behaviour Migration
self.resources Real SkillResources. self.resources.load_dialog / render_dialog / load_vocab / load_list_file / .value / .template / .word / .json all keep working. High-level methods (speak_dialog, voc_match, …) or ovos_spec_tools.LocaleResources directly.
self.dialog_renderer Real MustacheDialogRenderer. render(name, data) works unchanged. OVOSSkill.render_dialog (delegates to ovos_spec_tools.render).
self.find_resource(name, dirname=None, lang=None) Delegates to ovos_workshop.resource_files.find_resource. ovos_spec_tools.LocaleResources.find.
self.load_dialog_files(root=None) Silent no-op shim. Drop the call (LocaleResources is lazy).
self.voc_match_cache Live _voc_cache getter; setter emits warning + accepts dict. voc_match / voc_list cache internally.
runtime_requirements / network_requirements classproperty shims; subclass overrides honoured. Let the framework infer from the intent surface.

The mixin is a single import away from removal — when the deprecation window closes, dropping _LegacyResourcesMixin from OVOSSkill's bases (and deleting _legacy_resources.py) is the only change needed.

Other resource roles outside OVOS-INTENT-2 (.list, .value, .word, .template, .qml, skill-side .json) keep loading through the preserved self.resources handle.

What changed internally (no public-surface impact)

  • .dialog -> LocaleResources.load_dialog + ovos_spec_tools.render with vocabularies() for <voc> references. Drives render_dialog, speak_dialog, get_response, the skill.error flow.
  • .voc -> LocaleResources.vocabularies() (eager Adapt-keyword registration) and load_vocabulary (for voc_list / voc_match / remove_voc).
  • .intent / .entity -> OVOSSkill._locate_lang_file (closest_lang + os.walk); resolved path handed to intent_service.register_padatious_{intent,entity}.
  • .rx regex files -> OVOSSkill.load_regex_files is kept and self-contained; deprecation independent of (and outliving) the broader resource_files deprecation.
  • Language directory resolution in OVOSAbstractApplication.get_language_dir delegates to ovos_spec_tools.find_lang_dir (new public helper in ovos-spec-tools 0.6.0a1).
  • Workshop's internal word_connectors.json / euphony.json read via closest_lang resolution.

grep -rn 'resource_files|SkillResources|MustacheDialogRenderer|ResourceFile|RegexExtractor' ovos_workshop/ matches only inside resource_files.py and the legacy mixin.

One private removal

UniversalSkill._load_lang — a dead override constructing a SkillResources; never part of the public API (underscore-prefixed). The base-class load_lang (public, on every OVOSSkill) now returns a LocaleResources instead of a SkillResources, but caller patterns are identical and the load_* methods callers use exist on both.

Pin bumps

Dep Floor Why
ovos-utils >=0.11.1a1 standardize_lang_tag(macro=True) region-preserving fix (ovos-utils#377). Without it SessionManager rewrote session.lang from en-US to en on every message.
ovos-spec-tools >=0.6.0a1 LocaleResources.find / load_blacklist / load_vocabulary + empty-msg_type construction + find_lang_dir.
ovos_bus_client >=2.0.0a4 Session.deserialize keeps the region.

Tests

Unit-test coverage on the mixin:

  • test/unittests/skills/test_legacy_resources.py — every shim, every legacy file role, deprecation warnings, source-precedence chain, user-override > skill > core.
  • test/unittests/skills/test_legacy_skill_omnibus.py — one _LegacyOmnibusSkill exercising every deprecated API and every legacy resource file type in a single initialize() lifecycle. Single-file tripwire.
  • test/unittests/skills/test_regex_loading.py — Adapt regex via load_regex_files.

New ovoscope end-to-end tripwires for the highest-risk paths:

  • test/end2end/test_regex_skill.py — .rx → Adapt match.
  • test/end2end/test_universal_skill.pyUniversalSkill cross-language speak_dialog + voc_match + realistic Portuguese-user-with-English-skill round trip.
  • test/end2end/test_speak_dialog.pyspeak_dialogrender_dialogLocaleResources.load_dialogrender with {slot} + <voc>.
  • test/end2end/test_padacioso_intent_file.pyregister_intent_file_locate_lang_file → padacioso match.
  • test/end2end/test_padacioso_entity_file.pyregister_entity_file → bus event carrying the resolved .entity path + parsed samples. (Asserts on the registration plumbing, not slot-fill — .entity files in OVOS are training-data hints, not strict constraints.)
  • test/end2end/test_voc_match.pyvoc_match on vanilla OVOSSkill.

Migration path for skill authors

Skills migrate incrementally; each DeprecationWarning points at the spec-tools-backed replacement. No skill needs to change in this release cycle. The mixin and resource_files both have a one-major-release deprecation window.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Consistent dialog rendering helper for template substitution and vocabulary references.
    • Improved locale-aware resource discovery with better language fallback.
  • Bug Fixes

    • Corrected translation direction and language handling in multi-language skills.
    • Improved vocabulary matching and regex intent resolution reliability.
  • Deprecations

    • Legacy resource APIs and decorator patterns now emit runtime deprecation warnings; backwards-compatible shims retained.
  • Tests

    • Extensive unit and end-to-end tests for dialogs, intents, entities, voc matching, regexes, and multilingual behavior.
  • Chores

    • Excluded temporary test config from version control.

Review Change Stack

…ack-compat)

Part of the OVOS migration onto ovos-spec-tools
(OpenVoiceOS/architecture#7).

OVOSSkill and the rest of ovos_workshop now route resource access
through ovos_spec_tools.LocaleResources directly. The legacy
ovos_workshop.resource_files.SkillResources is no longer used
**internally** but stays shipped and stays available on every
OVOSSkill through a deprecation mixin — every skill in the
ecosystem keeps working.

## Back-compat surface preserved

All legacy entry points stay on OVOSSkill via the new
_LegacyResourcesMixin. Each emits a single DeprecationWarning
pointing at the spec-tools-backed replacement and routes through
the same cached SkillResources instance so behaviour is unchanged.

- self.resources           -> real SkillResources (legacy load_*_file roles intact)
- self.dialog_renderer     -> real MustacheDialogRenderer
- self.find_resource       -> resource_files.find_resource
- self.load_dialog_files   -> silent no-op (lazy load via LocaleResources)
- self.voc_match_cache     -> live _voc_cache getter + warning setter
- runtime_requirements / network_requirements classproperties

The mixin is a single import away from removal — when the
deprecation window closes, dropping _LegacyResourcesMixin from
OVOSSkill's bases (and deleting _legacy_resources.py) is the only
change needed.

Legacy resource roles outside OVOS-INTENT-2 (.list, .value, .word,
.template, .qml, skill-side .json) keep loading through the
preserved self.resources handle.

## What changed internally (no public-surface impact)

- .dialog rendering -> LocaleResources.load_dialog + render
  (with vocabularies() for <voc> references). Drives render_dialog,
  speak_dialog, get_response, the skill.error flow.
- .voc -> LocaleResources.vocabularies() (eager Adapt-keyword
  registration) and load_vocabulary (for voc_list / voc_match /
  remove_voc).
- .intent / .entity -> OVOSSkill._locate_lang_file (closest_lang +
  os.walk); the resolved path is handed to
  intent_service.register_padatious_intent / register_padatious_entity.
- .rx regex files -> OVOSSkill.load_regex_files is kept and
  self-contained; deprecation independent of (and outliving) the
  broader resource_files deprecation.
- Language directory resolution in
  OVOSAbstractApplication.get_language_dir delegates to
  ovos_spec_tools.find_lang_dir (new public helper from
  ovos-spec-tools 0.6.0a1).
- ovos_workshop's internal word_connectors.json / euphony.json
  read via closest_lang resolution.

grep -rn 'resource_files|SkillResources|MustacheDialogRenderer|ResourceFile|RegexExtractor' ovos_workshop/
matches only inside resource_files.py and the legacy mixin.

## One private removal

UniversalSkill._load_lang — a dead override constructing a
SkillResources; never part of the public API (underscore-prefixed).
The base-class load_lang (public, on every OVOSSkill) now returns
a LocaleResources instead of a SkillResources, but caller patterns
are identical and the load_* methods callers use exist on both.

## Pin bumps

- ovos-utils       >= 0.11.1a1 — standardize_lang_tag(macro=True)
  region-preserving fix (without it SessionManager rewrote
  session.lang from en-US to en).
- ovos-spec-tools  >= 0.6.0a1  — LocaleResources.find /
  load_blacklist / load_vocabulary semantics + the empty-msg_type
  construction allowance + find_lang_dir.
- ovos_bus_client  >= 2.0.0a4  — Session.deserialize keeps the
  region.

## Tests

Unit-test coverage on the mixin:
- test/unittests/skills/test_legacy_resources.py — every shim, every
  legacy file role, deprecation warnings, source-precedence chain,
  user-override > skill > core.
- test/unittests/skills/test_legacy_skill_omnibus.py — one
  _LegacyOmnibusSkill that exercises every deprecated API and every
  legacy resource file type in a single initialize() lifecycle.
- test/unittests/skills/test_regex_loading.py — adapt regex via
  load_regex_files.

New ovoscope end-to-end tripwires for the highest-risk paths:
- test/end2end/test_regex_skill.py — .rx -> adapt match (pre-existing).
- test/end2end/test_universal_skill.py — UniversalSkill cross-language
  speak_dialog + voc_match.
- test/end2end/test_speak_dialog.py — speak_dialog -> render_dialog
  -> LocaleResources.load_dialog -> render with {slot} + <voc>.
- test/end2end/test_padacioso_intent_file.py - register_intent_file
  -> _locate_lang_file -> padacioso match.
- test/end2end/test_padacioso_entity_file.py - register_entity_file
  -> bus event carrying the resolved .entity path + parsed samples.
- test/end2end/test_voc_match.py — voc_match on vanilla OVOSSkill.

## Migration path for skill authors

Skills migrate incrementally; each DeprecationWarning points at the
spec-tools-backed replacement. No skill *needs* to change in this
release cycle. The mixin and resource_files both have a
one-major-release deprecation window.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Warning

Review limit reached

@JarbasAl, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 5 minutes and 34 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6b86bf23-3172-47ca-b99a-6636fc2e5d55

📥 Commits

Reviewing files that changed from the base of the PR and between a5cd538 and a190a27.

📒 Files selected for processing (3)
  • ovos_workshop/decorators/__init__.py
  • ovos_workshop/skills/_legacy_resources.py
  • ovos_workshop/skills/ovos.py
📝 Walkthrough

Walkthrough

This PR migrates resource lookup/rendering to ovos-spec-tools LocaleResources, deprecates the legacy ovos_workshop.resource_files APIs (adding runtime deprecation warnings), adds a legacy compatibility mixin for OVOSSkill, updates language handling across skills, and adds extensive unit and end-to-end tests and locale fixtures.

Changes

Resource handling modernization and deprecation

Layer / File(s) Summary
Dependencies and core infrastructure
.gitignore, pyproject.toml, ovos_workshop/app.py
Added ovos-spec-tools to dependencies, bumped ovos-utils minimum, updated OVOSAbstractApplication.get_language_dir to use ovos_spec_tools.find_lang_dir/standardize_lang, and ignored test temp config directory.
Deprecation warnings in public decorator APIs
ovos_workshop/decorators/__init__.py
Added DeprecationWarning emissions to resting_screen_handler and homescreen_app decorator factories via warnings.warn() and updated docstrings documenting deprecation.
Resource files module refactoring and deprecation
ovos_workshop/resource_files.py
Marked module and legacy resource classes deprecated with _deprecated_public; switched DialogFile/IntentFile/VocabularyFile rendering and expansion to use ovos_spec_tools (expand/render), added DialogFile._load_raw(), updated language matching to ovos_spec_tools.lang_distance, and rewired SkillResources.dialog_renderer to lazily build Mustache templates while emitting deprecation warnings.
Legacy resource compatibility mixin
ovos_workshop/skills/_legacy_resources.py
Added _LegacyResourcesMixin exposing deprecated shims (resources, dialog_renderer, find_resource(), voc_match_cache, runtime_requirements, network_requirements, load_dialog_files) that emit DeprecationWarning and route through cached legacy SkillResources.
Core OVOSSkill modernization
ovos_workshop/skills/ovos.py
Refactored OVOSSkill to use ovos_spec_tools.LocaleResources (cached per root) for dialog/voc/intent/entity lookups and rendering, added render_dialog(name, data), separated query vs resource language (_resource_lang/_locale_resources), updated .voc/.rx loading/registration to use LocaleResources and spectools helpers, and wrapped homescreen/resting registrations with deprecation warnings.
UniversalSkill, converse, and util updates
ovos_workshop/skills/auto_translatable.py, ovos_workshop/skills/converse.py, ovos_workshop/skills/util.py
Added UniversalSkill._resource_lang returning internal_language, fixed translation call argument mapping in speak(), standardized converse language matching and intent file resolution with ovos_spec_tools, and replaced CoreResources JSON loading with a _load_workshop_json() helper that selects closest locale JSON.
Test locale fixtures and resources
test/end2end/locale/*, test/end2end/universal_locale/locale/*
Added and updated dialog, intent, voc, entity, and rx fixture files across en-US and pt-PT locales to support new and existing E2E tests.
End-to-end integration tests
test/end2end/test_*.py
Added multiple E2E tests verifying entity and intent file registration, regex intent matching, speak_dialog rendering with slot and voc expansion, voc_match behavior, and UniversalSkill resource-language decoupling across sessions.
Unit test updates and legacy resource coverage
test/unittests/skills/test_*.py
Updated unit tests to assert LocaleResources usage, added test_legacy_resources.py (broad legacy-shim coverage), test_legacy_skill_omnibus.py (omnibus legacy checks), and test_regex_loading.py (pin .rx behavior and deprecation timing).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A workshop hops to spec-tools new,
Old paths deprecated, but shims see it through,
Dialogs now render, languages align,
Tests hop in place to prove every line,
A little rabbit cheers: "Forward we stew!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 41.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: refactoring OVOSSkill resource loading to use ovos-spec-tools while maintaining backward compatibility. It is concise, specific, and directly reflects the main objective of the PR.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/drop-resource-files-usage

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 and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

Greetings, human! The automated checks are complete. 👾

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

📋 Repo Health

Checking for any potential repo regressions. 🔄

✅ All required files present.

Latest Version: 8.2.0a1

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

⚖️ License Check

Checking if the licenses are compatible with OVOS. 🧩

✅ No license violations found (49 packages).

License distribution: 12× MIT License, 9× Apache Software License, 7× MIT, 6× Apache-2.0, 2× BSD-3-Clause, 2× ISC License (ISCL), 2× PSF-2.0, 2× Python Software Foundation License, +7 more

Full breakdown — 49 packages
Package Version License URL
audioop-lts 0.2.2 PSF-2.0 link
build 1.5.0 MIT link
certifi 2026.5.20 Mozilla Public License 2.0 (MPL 2.0) link
charset-normalizer 3.4.7 MIT link
click 8.4.1 BSD-3-Clause link
combo_lock 0.3.1 Apache-2.0 link
filelock 3.29.0 MIT link
idna 3.16 BSD-3-Clause link
importlib_metadata 9.0.0 Apache-2.0 link
json-database 0.10.1 MIT link
kthread 0.2.3 MIT License link
langcodes 3.5.1 MIT License link
markdown-it-py 4.2.0 MIT License link
mdurl 0.1.2 MIT License link
memory-tempfile 2.2.3 MIT License link
ovos-config 2.1.1 Apache-2.0 link
ovos-number-parser 0.5.1 Apache Software License link
ovos-option-matcher-fuzzy-plugin 0.0.1 Apache Software License link
ovos-plugin-manager 2.4.0a2 Apache-2.0 link
ovos-spec-tools 0.6.0a1 Apache Software License link
ovos-utils 0.11.1a1 Apache-2.0 link
ovos-workshop 8.2.0a1 Apache-2.0 link
ovos-yes-no-plugin 0.3.0 Apache Software License link
ovos_bus_client 1.5.0 Apache Software License link
packaging 26.2 Apache-2.0 OR BSD-2-Clause link
padacioso 1.0.0 apache-2.0 link
pexpect 4.9.0 ISC License (ISCL) link
ptyprocess 0.7.0 ISC License (ISCL) link
pyee 12.1.1 MIT License link
Pygments 2.20.0 BSD-2-Clause link
pyproject_hooks 1.2.0 MIT License link
python-dateutil 2.9.0.post0 Apache Software License; BSD License link
PyYAML 6.0.3 MIT License link
quebra-frases 0.3.7 Apache Software License link
RapidFuzz 3.14.5 MIT link
regex 2026.5.9 Apache-2.0 AND CNRI-Python link
requests 2.34.2 Apache Software License link
rich 13.9.4 MIT License link
rich-click 1.9.7 MIT License

Copyright (c) 2022 Phil Ewels

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
| link |
| simplematch | 1.4 | MIT License | link |
| six | 1.17.0 | MIT License | link |
| standard-aifc | 3.13.0 | Python Software Foundation License | link |
| standard-chunk | 3.13.0 | Python Software Foundation License | link |
| typing_extensions | 4.15.0 | PSF-2.0 | link |
| unicode-rbnf | 2.4.0 | MIT License | |
| urllib3 | 2.7.0 | MIT | link |
| watchdog | 6.0.0 | Apache Software License | link |
| websocket-client | 1.9.0 | Apache Software License | link |
| zipp | 4.1.0 | MIT | link |

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

🔍 Lint

I've finished my task! Here's the data you need. 📊

ruff: issues found — see job log

🔒 Security (pip-audit)

Scanning for any potential privilege escalations. 🪜

✅ No known vulnerabilities found (68 packages scanned).

🔨 Build Tests

I've finished the digital carpentry on this PR. 🔨

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

Keeping the OVOS ecosystem thriving 🌿

…cept

Four more OVOSSkill methods now emit DeprecationWarning on external call,
plus the homescreen feature surface is being dropped entirely.

Framework-internal hot-path methods (callers: _startup only):
  - load_data_files  -> _load_data_files
  - load_vocab_files -> _load_vocab_files

Homescreen concept (deprecated entirely, no replacement planned):
  - register_homescreen_app    -> _register_homescreen_app
  - register_resting_screen    -> _register_resting_screen
  - @homescreen_app decorator
  - @resting_screen_handler decorator

The four method renames keep public-name shims that delegate to the
private implementation; framework callsites in _startup /
_register_app_launcher now use the private names directly. Subclass
overrides of the public name are no longer invoked by the framework.

Also drops the _caller_is_internal stack-walking suppression in
resource_files.py: deprecation warnings are valuable signal and must
not be hidden from workshop's own logs. Audit confirms no workshop
internals call the deprecated public surface except the legacy mixin
itself (downstream of explicit user opt-in via self.resources etc.).

Tests: test_legacy_skill_omnibus extended to exercise the four new
shims and assert their DeprecationWarnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ovos_workshop/skills/ovos.py (1)

739-763: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't mask the original startup failure when status is still unset.

If _init_settings() or bind() throws before self.status is assigned, the except path raises a second AttributeError here and hides the real cause. That's exactly what the current CI failures are showing.

💡 Suggested fix
         except Exception as e:
-            self.status.set_error(str(e))
+            if hasattr(self, "status"):
+                self.status.set_error(str(e))
+            else:
+                LOG.exception("Skill startup failed before ProcessStatus initialization")
             # If an exception occurs, attempt to clean up the skill
             try:
                 self.default_shutdown()
🤖 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 `@ovos_workshop/skills/ovos.py` around lines 739 - 763, The except block can
raise AttributeError when self.status was never set (e.g., if _init_settings()
or bind() failed); update the exception handler to avoid masking the original
error by guarding access to self.status and default_shutdown: check
hasattr(self, "status") and self.status is not None before calling
self.status.set_error(str(e)) or self.status.* methods, and protect
default_shutdown call similarly (or call it only if it exists), then re-raise
the original exception; reference symbols: self.status, _init_settings, bind,
default_shutdown, self.status.set_error.
🧹 Nitpick comments (1)
ovos_workshop/skills/util.py (1)

30-50: ⚡ Quick win

Cache locale JSON resolution to avoid repeated filesystem I/O.

_load_workshop_json currently rescans locale dirs and re-reads JSON on each call; join_word_list can hit this path frequently.

💡 Suggested refactor
+from functools import lru_cache
 import json
 import os
 from os.path import dirname, isdir, isfile, join
@@
+@lru_cache(maxsize=1)
+def _available_locale_dirs() -> Dict[str, str]:
+    return {lang: str(path) for lang, path in iter_locale_dirs(_LOCALE_ROOT)}
+
+@lru_cache(maxsize=64)
 def _load_workshop_json(name: str, lang: str) -> Optional[Dict]:
@@
-    available = dict(iter_locale_dirs(_LOCALE_ROOT))  # {lang_norm: Path}
+    available = _available_locale_dirs()  # {lang_norm: path}
@@
-    path = join(str(available[best]), f"{name}.json")
+    path = join(available[best], f"{name}.json")
🤖 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 `@ovos_workshop/skills/util.py` around lines 30 - 50, The function
_load_workshop_json repeatedly rescans locale dirs and reloads JSON on every
call (used frequently by join_word_list); cache results to avoid filesystem I/O
by memoizing the available locale mapping and parsed JSON per (name, lang) pair.
Specifically, keep a module-level cache (or use functools.lru_cache) for
iter_locale_dirs(_LOCALE_ROOT) output (used in available =
dict(iter_locale_dirs(...))) and for loaded JSON results of json.load(path)
keyed by (name, best) so subsequent calls return the cached dict instead of
re-reading the file; ensure cache is invalidated/refreshable if needed (e.g.,
provide a clear/reset helper) and preserve current behavior when files are
missing.
🤖 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 `@ovos_workshop/app.py`:
- Line 4: The module removed the join import but settings_path still uses join,
causing runtime/lint failures; re-add join to the import list so settings_path
can use it—specifically update the top-level import that currently reads "from
ovos_spec_tools import find_lang_dir, standardize_lang" to also import join so
the function/settings_path and any uses of join resolve correctly.

In `@ovos_workshop/skills/_legacy_resources.py`:
- Around line 59-69: The _legacy_skill_resources method currently snapshots
self.lang into SkillResources and never updates; change it to use
self._resource_lang when constructing SkillResources and add logic to
invalidate/rebuild the cached self._skill_resources_compat whenever
self._resource_lang changes (store the last used lang or compare current
self._resource_lang to the lang on the cached SkillResources), so callers like
resources, dialog_renderer and find_resource get a SkillResources instance
matching the current _resource_lang; update the same pattern in the other
shim(s) referenced (lines 127-147) to ensure cache is refreshed on language
change.

In `@ovos_workshop/skills/ovos.py`:
- Around line 744-748: Startup now calls private implementations
(_load_data_files, _register_skill_json, _register_decorated,
_register_app_launcher, _register_resting_screen) which bypasses subclass
overrides; change calls so public methods (load_data_files,
register_skill_json/register_decorated if present, register_homescreen_app,
register_resting_screen) are invoked first and fall back to their underscored
counterparts if the public method is not overridden. Update _startup,
_register_app_launcher, and any other locations (e.g., around lines 791-793 and
2403-2439) to call the public shim methods like load_data_files and
register_homescreen_app instead of directly calling
_load_data_files/_register_app_launcher, preserving subclass override
compatibility while keeping private implementations as fallbacks.

In `@ovos_workshop/skills/util.py`:
- Around line 49-50: The locale JSON loader opens files using the system default
encoding; change the open call in ovos_workshop/skills/util.py to explicitly use
UTF-8 by passing encoding='utf-8' (e.g., in the with open(path, ...) statement
that wraps json.load), so that parsing localized content works correctly across
environments.

In `@test/end2end/test_regex_skill.py`:
- Around line 72-75: The on_speak handler currently sets speak_event for every
speak message which can unblock the test prematurely; change on_speak (and the
test wiring around seen_speaks/speak_event) to only set speak_event when the
utterance matches the expected output (e.g., compare
msg.data.get("utterance","") against the expected string/regex or check
substring) and still append seen_speaks as needed, so the wait only wakes for
the regex handler's emission.

In `@test/unittests/skills/test_legacy_skill_omnibus.py`:
- Around line 184-185: The tearDownClass currently swallows all exceptions from
cls.skill.default_shutdown() with "except Exception: pass"; change this to catch
Exception as e and handle it instead of silencing—either re-raise after logging
or call a test logger/assertFail to surface the error; specifically update the
tearDownClass method to wrap cls.skill.default_shutdown() in try/except
Exception as e and call something like logging.exception or raise to ensure
failures are reported rather than ignored.

---

Outside diff comments:
In `@ovos_workshop/skills/ovos.py`:
- Around line 739-763: The except block can raise AttributeError when
self.status was never set (e.g., if _init_settings() or bind() failed); update
the exception handler to avoid masking the original error by guarding access to
self.status and default_shutdown: check hasattr(self, "status") and self.status
is not None before calling self.status.set_error(str(e)) or self.status.*
methods, and protect default_shutdown call similarly (or call it only if it
exists), then re-raise the original exception; reference symbols: self.status,
_init_settings, bind, default_shutdown, self.status.set_error.

---

Nitpick comments:
In `@ovos_workshop/skills/util.py`:
- Around line 30-50: The function _load_workshop_json repeatedly rescans locale
dirs and reloads JSON on every call (used frequently by join_word_list); cache
results to avoid filesystem I/O by memoizing the available locale mapping and
parsed JSON per (name, lang) pair. Specifically, keep a module-level cache (or
use functools.lru_cache) for iter_locale_dirs(_LOCALE_ROOT) output (used in
available = dict(iter_locale_dirs(...))) and for loaded JSON results of
json.load(path) keyed by (name, best) so subsequent calls return the cached dict
instead of re-reading the file; ensure cache is invalidated/refreshable if
needed (e.g., provide a clear/reset helper) and preserve current behavior when
files are missing.
🪄 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

Run ID: cbb8676b-dfdc-4473-8eae-926b41640555

📥 Commits

Reviewing files that changed from the base of the PR and between 779e54b and 4e53715.

📒 Files selected for processing (32)
  • .gitignore
  • ovos_workshop/app.py
  • ovos_workshop/decorators/__init__.py
  • ovos_workshop/resource_files.py
  • ovos_workshop/skills/_legacy_resources.py
  • ovos_workshop/skills/auto_translatable.py
  • ovos_workshop/skills/converse.py
  • ovos_workshop/skills/ovos.py
  • ovos_workshop/skills/util.py
  • pyproject.toml
  • test/end2end/__init__.py
  • test/end2end/locale/en-US/drink.entity
  • test/end2end/locale/en-US/greet.dialog
  • test/end2end/locale/en-US/play.rx
  • test/end2end/locale/en-US/salutation.voc
  • test/end2end/locale/en-US/wave.intent
  • test/end2end/locale/en-US/yes.voc
  • test/end2end/test_padacioso_entity_file.py
  • test/end2end/test_padacioso_intent_file.py
  • test/end2end/test_regex_skill.py
  • test/end2end/test_speak_dialog.py
  • test/end2end/test_universal_skill.py
  • test/end2end/test_voc_match.py
  • test/end2end/universal_locale/locale/en-US/affirmative.voc
  • test/end2end/universal_locale/locale/en-US/echo.dialog
  • test/end2end/universal_locale/locale/en-US/fact.intent
  • test/end2end/universal_locale/locale/pt-PT/fact.intent
  • test/unittests/skills/test_base.py
  • test/unittests/skills/test_legacy_resources.py
  • test/unittests/skills/test_legacy_skill_omnibus.py
  • test/unittests/skills/test_ovos.py
  • test/unittests/skills/test_regex_loading.py

Comment thread ovos_workshop/app.py
Comment thread ovos_workshop/skills/_legacy_resources.py Outdated
Comment thread ovos_workshop/skills/ovos.py Outdated
Comment thread ovos_workshop/skills/util.py Outdated
Comment on lines +72 to +75
def on_speak(msg):
seen_speaks.append(msg.data.get("utterance", ""))
speak_event.set()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Gate the wait event on the expected utterance to prevent flakiness.

on_speak sets the event for every speak message. If unrelated speech arrives first, the wait can unblock before the regex handler emits its output.

Suggested fix
         def on_speak(msg):
-            seen_speaks.append(msg.data.get("utterance", ""))
-            speak_event.set()
+            utt = msg.data.get("utterance", "")
+            seen_speaks.append(utt)
+            if "playing music" in utt:
+                speak_event.set()
🤖 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/end2end/test_regex_skill.py` around lines 72 - 75, The on_speak handler
currently sets speak_event for every speak message which can unblock the test
prematurely; change on_speak (and the test wiring around
seen_speaks/speak_event) to only set speak_event when the utterance matches the
expected output (e.g., compare msg.data.get("utterance","") against the expected
string/regex or check substring) and still append seen_speaks as needed, so the
wait only wakes for the regex handler's emission.

Comment on lines +184 to +185
except Exception:
pass
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether silent broad-exception swallowing exists in this test module
rg -nP 'except\s+Exception:\s*\n\s*pass' test/unittests/skills/test_legacy_skill_omnibus.py

Repository: OpenVoiceOS/ovos-workshop

Length of output: 51


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="test/unittests/skills/test_legacy_skill_omnibus.py"
# Print the claimed area with line numbers
nl -ba "$file" | sed -n '165,205p'

Repository: OpenVoiceOS/ovos-workshop

Length of output: 112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="test/unittests/skills/test_legacy_skill_omnibus.py"
awk 'NR>=165 && NR<=205 { printf "%d:%s\n", NR, $0 }' "$file"

Repository: OpenVoiceOS/ovos-workshop

Length of output: 2046


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="test/unittests/skills/test_legacy_skill_omnibus.py"
# Show the import section and any warnings usage
rg -n '^\s*(import|from)\s+warnings|warnings\.' "$file" || true
head -n 60 "$file"

Repository: OpenVoiceOS/ovos-workshop

Length of output: 2616


Avoid silently swallowing teardown exceptions in tearDownClass.

In test/unittests/skills/test_legacy_skill_omnibus.py lines 184–185, except Exception: pass hides failures from cls.skill.default_shutdown().

Proposed fix
-        except Exception:
-            pass
+        except Exception as e:
+            warnings.warn(
+                f"default_shutdown failed during teardown: {e!r}",
+                RuntimeWarning
+            )
🧰 Tools
🪛 Ruff (0.15.14)

[error] 184-185: try-except-pass detected, consider logging the exception

(S110)


[warning] 184-184: Do not catch blind exception: Exception

(BLE001)

🤖 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/skills/test_legacy_skill_omnibus.py` around lines 184 - 185,
The tearDownClass currently swallows all exceptions from
cls.skill.default_shutdown() with "except Exception: pass"; change this to catch
Exception as e and handle it instead of silencing—either re-raise after logging
or call a test logger/assertFail to surface the error; specifically update the
tearDownClass method to wrap cls.skill.default_shutdown() in try/except
Exception as e and call something like logging.exception or raise to ensure
failures are reported rather than ignored.

JarbasAl and others added 2 commits May 26, 2026 01:31
- app.py: restore missing `from os.path import join` (CI red).
- _LegacyResourcesMixin: key the SkillResources cache on
  (res_dir, _resource_lang) so UniversalSkill (resources in
  internal_language) and plain OVOSSkill instances with
  session-language changes both see the right files. Previously
  snapshot self.lang on first access, breaking multilingual back-compat.
- skills/ovos.py: revert load_data_files / load_vocab_files /
  register_homescreen_app / register_resting_screen renames to
  preserve subclass overrides. _startup keeps calling the public
  names so legacy override patterns continue to participate in
  startup. Homescreen pair retains in-body deprecation warnings
  (homescreen concept is being removed entirely).
- skills/util.py: open localized JSON with explicit utf-8 encoding.
- test_legacy_skill_omnibus: drop the four-shim assertions, keep
  the homescreen-concept deprecation assertion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Every deprecated surface now emits both a DeprecationWarning (visible
to IDEs, -W error::DeprecationWarning, runtime warning filters) AND a
LOG.warning line so consumers tailing logs cannot miss them.

_legacy_resources.py
  - voc_match_cache setter: add @deprecated log alongside existing
    warnings.warn.
  - network_requirements: add warnings.warn alongside existing
    LOG.warning.
  - new public-name shims: register_homescreen_app and
    register_resting_screen — both signals + @deprecated decorator,
    delegate to the _-prefixed implementation in OVOSSkill.

skills/ovos.py
  - load_regex_files: add LOG.warning alongside the existing
    warnings.warn (gated on a .rx file being present so silent
    skills stay silent).
  - register_homescreen_app -> _register_homescreen_app: framework
    calls the private name; emits warn + LOG only on the gated
    @homescreen_app path so non-users stay silent.
  - register_resting_screen -> _register_resting_screen: same
    pattern, only emits when a @resting_screen_handler-tagged
    method is found.
  - _startup / _register_app_launcher updated to call the private
    names. Subclass overrides of register_homescreen_app /
    register_resting_screen no longer participate in startup —
    acceptable because the whole concept is being removed.

decorators/__init__.py
  - @homescreen_app and @resting_screen_handler now log + warn.

Co-Authored-By: Claude Opus 4.7 (1M context) <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