Release 9.8.0a1 - #626
Closed
github-actions[bot] wants to merge 287 commits into
Closed
Release 9.8.0a1#626github-actions[bot] wants to merge 287 commits into
github-actions[bot] wants to merge 287 commits into
Conversation
* 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>
2.x only removed the bundled hivemind agent protocol + messagebus solver (ovos-bus-client#207); no API break (#215). Widening keeps 1.x as default while permitting 2.x, unblocking the HiveMind stack's 2.x adoption.
OVOSSkill.speak() now emits SpecMessage.SPEAK ('ovos.utterance.speak')
instead of the legacy 'speak' bus topic. The MessageBusClient namespace
migration also-emits the legacy 'speak' topic (emit_legacy, default on),
so existing audio consumers still receive it.
Add ovos-spec-tools>=0.9.0a1 direct dep; bump ovos-bus-client>=2.2.0a1.
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…427) * feat!: intent layers gate via intent context, not enable/disable Redesign IntentLayers so a layer maps to an intent-context token instead of mutating the global enabled/disabled intent set. - activate_layer -> skill.set_context(<layer token>) - deactivate_layer -> skill.remove_context(<layer token>) - reset() (kept aliased as disable()) removes every active layer context `@layer_intent` now injects the layer context token as a `.require()` on the IntentBuilder, so the intent only validates while its layer is active. Intents stay registered for the skill lifetime; gating is per-session via adapt context, with no detach/attach churn. resets_layers() now calls reset(); the enable_intent/disable_intent paths are gone from the layer machinery. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: game skills converse via context-gated layers + skill_will_match probe Make ConversationalGameSkill work with the new context-gated layers: - OVOSGameSkill now also inherits ConversationalSkill so the converse ping/request bus handlers are actually wired for game skills (they were not, so converse never ran for games). - Add OVOSSkill.skill_will_match(): a read-only probe over intent.service.intent.get that reports whether one of THIS skill's intents would match an utterance under the current session context. Supports exclude_pipeline so a conversing skill can skip the converse stage and avoid re-entrancy. - ConversationalGameSkill.can_converse() returns True only while playing AND no context-gated layer intent would match (so layer intents are handled by adapt, not swallowed by converse). converse() uses the same probe. calc_intent() now ignores non-intent-parser pipeline results. - on_play_game restart semantics doc fix in the game base. Add a 4-layer demo skill e2e (ovoscope MiniCroft / FakeBus) proving the layer mechanism: utterances advance layer0->layer3, only the active layer's intent matches at each step (others are gated off via missing context), always-on intents match throughout, and reset removes the context so layer intents stop matching. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: session-aware playback + per-session layer gating (OCP is session aware) OCP tracks player state per session, so game/playback skills must too. - OVOSCommonPlaybackSkill: _playing/_paused Events -> per-session sets (_playing_sessions/_paused_sessions) keyed by SessionManager.get(msg) .session_id. New get_session_id(), is_playing_in/is_paused_in, playing_sessions; is_playing/is_paused properties now resolve the current session. OCP play/pause/resume/stop handlers key state by session and reply with message.reply so the session round-trips. - OVOSGameSkill.stop_game is per-session; on_pause_game/on_resume_game no longer mutate paused state (the framework does, per session); on_game_command(self, utterance, lang, message=None) - the message (and thus session) is now passed so games can key per-session state. - IntentLayers.is_active(layer, session=None) reads the layer's context token from the session (the per-session source of truth), falling back to the skill-level active_layers when no session is resolvable. This makes layer gating correct under concurrent multi-session play. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test: adapt layer tests to context gating - common_play: assert per-session playback state (no playing/paused sessions on init) instead of the removed _playing/_paused Event objects - intent_layers_e2e: restore the global SessionManager.bus in tearDownClass so the stopped MiniCroft bus does not leak into get_response/killable intent tests sharing the class-level SessionManager Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: add ovos-adapt-parser to test deps for layer e2e pipeline The intent-layers e2e test drives a live ADAPT pipeline via MiniCroft; CI installs only the declared test extras, where ovos-adapt-parser (which provides the ovos-adapt-pipeline-plugin matcher) was missing, so the pipeline matched nothing ('Unknown pipeline matcher'). Add it to both the [test] extra and requirements/test.txt. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: bump ovos-adapt-parser test dep to >=1.3.1a1 for workshop 8.x Stable ovos-adapt-parser 1.0.9 caps ovos-workshop<8.0.0, which conflicts with this branch (8.3.0a1) and made the install unresolvable in CI. 1.3.1a1 relaxes the cap to <9.0.0; pin it as the floor (prerelease floor pin) so pip resolves it without --pre. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor: keep #427 to IntentLayers only; game-skill + OCP playback move to a stacked PR IntentLayers stays standalone and backwards-compatible for decorator consumers (disable() aliases reset(); all decorators + methods preserved). The session-aware game-skill + OCPCommonPlayback changes move to feat/gameskill-and-ocp-deprecation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: skill_will_match accepts a Session to probe under that session's context Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
padacioso 2.0 (OVOS-INTENT-1 grammar) is compatible; validated end-to-end in the OVOS spec-compliance harness against the workshop-9 stack.
…#432) * refactor: re-export intent-definition primitives from ovos-spec-tools Intent, IntentBuilder and open_intent_envelope are now re-exported from ovos-spec-tools (OVOS-INTENT-4 adapt-free keyword-intent definition model) instead of being defined locally as adapt metaclass wrappers. Skills keep their `from ovos_workshop.intents import IntentBuilder` import; the canonical implementation lives in the spec. IntentServiceInterface and the munge_* helpers are unchanged — they consume the re-exported definitions. The spec-tools IntentBuilder is API-compatible (.require/.optionally/.one_of/.exclude/.build/.name) and Intent keeps the same attribute surface (name/requires/at_least_one/optional/excludes). The local matching logic (Intent.validate / _resolve_one_of) is dropped: per OVOS-PIPELINE-1 matching is an engine-plugin concern, not part of the definition primitive. test_intent.py (which exercised that matching logic) is removed accordingly. pyproject: bump ovos-spec-tools floor to >=0.14.0a1 (the version carrying the intent primitives). CI pre-installs the unpublished spec-tools branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: pin published ovos-spec-tools>=0.16.1a2, drop git-dep Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * Update build_tests.yml --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
) These are NOT spec topics — they are an internal ovos-workshop -> ovos-core synchronization signal (workshop reporting 'I started/ended/errored' running a handler). They exist only because skills run out-of-process from the orchestrator; if core manipulated skill objects directly this bus round-trip would not be needed. ovos-core consumes them as a private done-signal to emit the authoritative PIPELINE-1 §8 spec trio (ovos.intent.handler.*); the legacy names are permanently ovos-workshop event-wrapper signals and do not bridge to the spec namespace (spec-tools MIGRATION_MAP excludes the trio). Doc-only.
STOP-1 §4.3 (as amended by #189): "The orchestrator dispatches <target_skill_id>:stop per PIPELINE-1 §7, firing the handler-lifecycle trio (...). The stop handler completes like any dispatched handler (PIPELINE-1 §8): the orchestrator alone emits .complete on normal return or .error on exception, and that terminal event resolves the stop round." PIPELINE-1 §8 defines that trio as the orchestrator's ovos.intent.handler.{start,complete,error}, which ovos-core derives from a private workshop->core done-signal (mycroft.skill.handler.{start,complete,error}) emitted around every other dispatched handler via OVOSSkill.add_event(handler_info=...). The targeted <skill_id>.stop registration was the one dispatch that never passed handler_info, so ovos-core's IntentDispatcher never saw a completion signal for it and had no faster path than the PIPELINE-1 §8.3 timeout. Route it through the same HandlerLifecycle wrapper as every other dispatched handler, stamping data["intent_name"] = "stop" so ovos-core's _resolve_entry can disambiguate this dispatch from any other in-flight dispatch for the same skill_id (LIFO-by-skill_id alone is ambiguous when a stop races an already-running intent handler). The existing .stop.response reply is unchanged (STOP-1 §4.2). STOP-1 §5.3/§9 also requires a skill performing user-visible activity to subscribe to the ovos.stop global broadcast, not only its own targeted <skill_id>.stop dispatch. The killable_event/killable_intent decorators only listened on <skill_id>.stop; ovos.stop is now added alongside for one deprecation cycle, wired through its own closure because ovos.stop is a namespace-migrated topic and the shared bus's legacy<->ovos.* dedup guard is keyed per handler object, so reusing the existing abort closure would have folded it into the same dedup entry as the non-migrated topics and hijacked their removal (caught by a full-suite regression: test_no_leak_on_natural_completion). That same test is extended to assert the ovos.stop listener is also cleaned up on natural completion, not just the legacy <skill_id>.stop one — verified by deleting the corresponding bus.remove("ovos.stop", ...) line and confirming the extended assertion fails, then restoring it. Fail-before: reverting the two source files and re-running the new/ modified regression tests makes test_handle_stop and test_skill_stop_ovos_stop_broadcast fail (0 handler.start/.complete signals observed; ovos.stop never aborts the killable thread), while test_no_leak_on_natural_completion still passes both ways. A minimal IntentDispatcher+OVOSSkill integration probe on a FakeBus confirms the end-to-end effect: dispatching "<skill_id>:stop" resolves within microseconds with the fix and never resolves within a 3s window without it. Full suite on a fresh clone, single worker, foreground: 690 passed, 0 failed, reproduced across two consecutive runs; no order-dependent flake observed in this environment. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
The broadcast-poll candidacy test in _handle_converse_broadcast_ack read
session.active_handlers, but OVOS-CONVERSE-1 §9.3 requires the handler to
check "whether its own skill_id appears in context.session.converse_handlers"
and §2.1 states that converse_handlers "is distinct from
session.active_handlers". A skill named in converse_handlers but not
active_handlers (or vice versa) got the wrong answer to ovos.converse.ping.
The same handler silently suppressed its pong when it held
session.response_mode. §2.1's "Response-mode holder exemption" only says the
holder MUST NOT be evicted or pruned "so the §2.2 identity invariant can
recognise it"; there is no rule that the holder stays silent on a poll, so
the early return was deleted and the holder now answers like any other
listed candidate.
_handle_converse_request also carried a version probe importing
ovos_core.version.OVOS_VERSION_TUPLE to special-case cores older than
2.0.3. Per the backcompat target ("latest-alpha x latest-stable, for ONE
stable cycle"), cores that old are out of scope, so the dead branch and its
try/except ImportError were removed and the reply-derived routing path
always runs.
Fail-before: reverting only the source change against the new/updated tests
in test/unittests/skills/test_converse_extended.py produced 8 failures
(test_candidacy_is_converse_handlers_not_active_handlers,
test_response_mode_holder_still_pongs,
test_converse_request_does_not_import_ovos_core, and five pre-existing
broadcast-poll tests updated to seed converse_handlers instead of
active_handlers). Re-applying the fix turned all 8 green; the full suite
(test/unittests) passed 678/678 after the fix.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
…er (INTENT-4 §6.1, INTENT-1 §5.6) (#599) A template may type a slot as `{type:name}` (OVOS-INTENT-1 §3.4). The type prefix is not part of the slot name, so the samples that reach an engine must carry the bare `{name}` and the declared types travel separately in the OVOS-INTENT-4 §6.1 registration's optional `slot_types` map. register_template reads the map with ovos-spec-tools' declared_slot_types and then folds the prefixes away with strip_type_prefixes, before validation and before either emit, so the legacy padatious registration and the spec topic receive byte-identical bare-name samples and an unregistered type degrades with a single warning. register_intent_file's slot-blacklist scan takes slot names from declared_slots instead of a brace regex that captured `color:shade` and silently stopped matching a sibling `.blacklist`. Skills read the values an engine computed with two message-data reads on the skill base: typed_slot(message, name) takes the §5.6 entry whose `surface` equals the slot value (first entry on repeats), and typed_slots(message, type) returns every entry of a type in span order. Both degrade to None / [] on a missing key, a non-dict map, non-list entries or a malformed entry. A slot's declared type comes from the registration bookkeeping, which keeps the `slot_types` map alongside the samples of each registered intent, so a surface string several engines reported under different types resolves to the type the intent declared for that slot. A slot with no declared type falls back to searching every registered type in REGISTERED_TYPES order. Fail-before: 19 of 21 new tests fail against the unfixed source (samples keep `{color:shade}`, no `slot_types` key, no helper), all pass after. Full suite green: 701 unit + 13 end2end, plus a GC-timing flake in test_abstract_app that reproduces the same way on dev. Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
remove_intent forwarded the deregister off the ambient dug message and
kept whatever context.skill_id that message carried, filling in
self.skill_id only when none was present. A skill disabling its own
intent while handling another component's message therefore emitted
{skill_id: "victim.skill"} under context.skill_id = "other.skill".
OVOS-INTENT-4 §3.2 requires payload skill_id to equal context.skill_id
on ovos.intent.deregister, and consumers enforcing that rule (and the
detach_intent legacy twin the bus client derives from it) drop the
skill's own detach as a forgery.
A skill only ever detaches its own intents, so the forward now always
carries self.skill_id in context; the dug message is left untouched.
Regression test: remove_intent called from a handler whose message
carries context.skill_id = "other.skill". Before the fix the emitted
context.skill_id was 'other.skill' (AssertionError: 'other.skill' !=
'test.skill'); after it is the calling skill's id and the ambient
message is unmodified.
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
OVOS-STOP-1 §4.2 makes `ovos.stop.ping` the stop cascade's poll topic and treats a handler that does not subscribe to it as `can_handle: false` for that round. OVOSSkill subscribed only to the pre-spec per-skill `<skill_id>.stop.ping`, so every skill's stoppability rested on the compatibility twin ovos-core still emits alongside the broadcast; once that twin goes, generic stop falls back to the recency rule for the whole fleet. OVOSSkill now subscribes to both pings and answers on `ovos.stop.pong` with the §4.2 payload. A core emitting both in one round draws exactly one pong: STOP-1 §4.2 binds the poll to PIPELINE-1 §4.5, which keys round state by the `session_id` of `context.session` and by `context.utterance_id` (§9.1.1) — both propagate to either ping by reply derivation — and rules that the first valid pong per candidate wins. A pre-spec core that stamps no `utterance_id` names no round, so its pings are answered one for one as before. Fail-before: the broadcast test fails on dev (0 pongs, nothing subscribes); with the subscription but no dedupe the single-pong test fails 2 != 1; a session-only round key fails two rounds in one session, 1 != 2. Suite: 708 passed, 1 pre-existing failure in test_scheduler_delegation (KeyError 'owner', PR #608). Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
OVOS-INTENT-4 §8.6 fixes `ovos.skill.loaded`: `{"skill_id": "...",
"capabilities": ["converse", "fallback"]}`, session-keyed via
context.session.session_id, capability vocabulary limited to
fallback/common_query/converse.
skill_launcher.py's load-status announcement kept the legacy
`mycroft.skills.loaded` payload unchanged and, beside it, now emits
`ovos.skill.loaded` through the loaded instance's own bus so
context.skill_id rides along and the bus auto-fills context.session.
OVOSSkill.on_ready_status re-emits the same announcement, covering
cold-start recovery when a skill process outlives a core restart.
Capabilities are read off the instance, never guessed: fallback via
isinstance(FallbackSkill), common_query via the _cq_handler a
@common_query-decorated method sets at bind time, converse via
isinstance(ConversationalSkill) -- the only base that wires the
CONVERSE-1 §4 surface (ovos.converse.ping, <skill_id>.converse.ping/
.request). A callable-attribute check was rejected: OVOSGameSkill and
ConversationalGameSkill define their own converse() outside
ConversationalSkill's MRO, and that surface never binds for them, so
"has a converse method" would announce a capability the skill cannot
answer on. The helper lives in ovos_workshop/skills/capabilities.py so
tests can assert its return value directly.
10 tests in test/unittests/test_ovos_skill_loaded.py assert the
capability values (not just that a message fired) for a plain skill
([]), a FallbackSkill (["fallback"]), a common-query skill
(["common_query"]), a ConversationalSkill (["converse"]), and a game
skill that defines converse() outside ConversationalSkill's MRO ([],
proving the callable-attribute check was rejected), plus the unchanged
legacy payload and the readiness re-announcement. Full suite 728
passed, no regressions.
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Human review requested! This supersedes any earlier open release proposal for this repository.