Skip to content

Release 0.3.0a2#39

Open
github-actions[bot] wants to merge 10 commits into
masterfrom
release-0.3.0a2
Open

Release 0.3.0a2#39
github-actions[bot] wants to merge 10 commits into
masterfrom
release-0.3.0a2

Conversation

@github-actions

Copy link
Copy Markdown

Human review requested!

JarbasAl and others added 10 commits April 17, 2026 16:13
…locale typos (#32)

* refactor: migrate language matching and template expansion to ovos-spec-tools

The deprecated ovos_utils.lang.standardize_lang_tag stripped the region
subtag, and the prior chain (`standardize_lang_tag("en-US")` ->
`"en"`, then `langcodes.closest_match("en", ["en-US", ...])`)
was failing to find a usable match for some inputs, leaving the cancel
transformer silent and the utterance falling through to the
intent-failure error sound. Switching the whole chain to the
spec-tools conformant implementations:

- `ovos_spec_tools.standardize_lang` preserves the region subtag
  (`en-US` -> `en-US`).
- `ovos_spec_tools.closest_lang` gates on the < 10 distance threshold
  internally and returns None when no candidate is close enough,
  replacing the manual score check.
- `ovos_spec_tools.expand` replaces the deprecated
  `ovos_utils.bracket_expansion.expand_template`.

Also fix the it-IT locale: `(|dimentica|...)` had an empty leading
alternative on a standalone group, which would have matched every
utterance via endswith(""). Spec-tools' strict OVOS-INTENT-1 §3.6
expansion correctly rejects it; drop the empty alternative.

Verified: 44 unit tests + the end2end `test_cancel_match` on
ovos-core both pass.

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

* refactor: use LocaleResources for cancel.intent lookup

Replace the hand-rolled locale discovery + file read + bracket expansion
with one `LocaleResources(skill_locale=...).load_intent("cancel", lang)`
call. `LocaleResources` applies the OVOS-INTENT-2 §2.1 source-precedence,
the §2.2 smart language fallback (`closest_lang` gated at distance < 10),
the §3 file reader (UTF-8, BOM-stripped, comment/blank-line filtered),
and the §3.6 sentence-template expansion — the same composite operation
this plugin was performing by hand.

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

* refactor: rename cancel.intent -> cancel.voc (correct OVOS-INTENT-2 role)

The plugin matches phrases via `endswith`, not full utterance
templates with slots, so the resource is a vocabulary set per
OVOS-INTENT-2 §4.3, not an intent template per §4.1. Switch to
`LocaleResources.load_vocabulary("cancel", lang)` to match.

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

* docs: add cancel.intent in each locale to document the matching pattern

The plugin still does suffix-matching against cancel.voc at runtime;
this .intent file is informational and not consumed. It exists so the
intent the plugin enforces is explicit in the locale folder for anyone
reading it: `[{utterance}] <cancel>` — anything ending with a cancel
phrase, plus a standalone cancel with no leading utterance.

Verified via spec-tools' LocaleResources.load_intent — the
`<cancel>` reference resolves against the sibling cancel.voc and
`[{utterance}]` produces both with-prefix and standalone samples.

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

* ci: add ovoscope end-to-end CI for the cancel transformer

The plugin's behaviour is observable only end-to-end (bus + intent
service + audio service), so unit tests can't catch the kind of
regression that surfaced on ovos-core's ovoscope: cancel matching
silently breaking and falling through to intent-failure. Add a local
ovoscope job so this repo gates on the same observation rather than
relying on downstream ovos-core to catch it.

Adds:
- `.github/workflows/ovoscope.yml` — reusable workflow from
  OpenVoiceOS/gh-automations, parameterised the same way ovos-core's
  is (Padatious + Adapt required, bus-coverage on).
- `test/end2end/test_cancel_plugin.py` — two ovoscope cases:
  cancel mid-sentence (the previously regressed case) and
  cancel-suffix on an arbitrary utterance.
- pyproject `[test]` extras: ovoscope + ovos-skill-hello-world.

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

* ci: scope build/coverage to unit tests, drop intent-engine reqs from ovoscope

- build-tests + coverage: target test/unittests/ only. The new
  test/end2end/ directory needs ovoscope (test extras), not the dev
  extras these workflows install.
- ovoscope: drop require_adapt / require_padatious / system_deps. The
  cancel transformer fires before intent matching; the ovoscope cases
  here do not depend on any intent engine.

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

* fix(test): enable the cancel transformer in MiniCroft's config before boot

OVOS's UtteranceTransformersService loads a plugin only when its
entry-point name appears in `Configuration().utterance_transformers`
(ovos_core/transformers.py:29); otherwise the plugin is silently
skipped. The default test config used by MiniCroft does not include
`ovos-utterance-cancel-plugin`, so the transformer never fired in
CI and the utterance fell through to the intent-failure error sound.

Set the key on Configuration() in setUp, before `get_minicroft` is
called. MiniCroft's `isolate_config` clears the user's XDG configs,
so this assignment only affects the test process.

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

* fix: restore entry-point name to match OVOS default config; read lang from session

Three real bugs surfaced by the new ovoscope tests:

1. **Entry-point name regression.** Dev had renamed the transformer
   entry point to `ovos-utterance-cancel-plugin`, but OVOS's default
   `mycroft.conf` references `ovos-utterance-plugin-cancel` under
   `utterance_transformers`. `UtteranceTransformersService`
   (ovos_core/transformers.py:29) silently skips any plugin whose name
   is not in the config — so the rename silently broke the plugin on
   every default install. Restore the historic name.

2. **`lang` lookup was wrong.** `context` here is the OVOS-MSG-1
   `message.context` dict; the normative language field lives at
   `context["session"]["lang"]` (§4), not at `context["lang"]`.
   ovos-core's IntentService also copies it to a top-level
   `context["lang"]` for legacy callers, but treating that as
   authoritative breaks for any consumer (HiveMind, tests, alt
   bus-clients) that hands the transformer a Message without that
   pre-processing. Prefer `session.lang`; fall back to legacy
   `context.lang`; default `"en-US"` if neither is set.

3. **`None` lang crash.** `context.get("lang", default)` returns
   `None` when the key exists with a `None` value — only a missing
   key triggers the default. `standardize_lang(None)` raises
   `AttributeError`, which the transformer service swallows as a
   warning — the plugin silently no-ops. Use `or` so an explicit
   `None` resolves to the default.

ovoscope test/end2end/test_cancel_plugin.py now covers all three:
- positive cancel-mid-sentence + cancel-suffix
- negative gate: with the plugin disabled in config, the same
  cancel-suffix falls through to intent failure — proving the
  positive cases are observing the plugin itself
- smoke: utterance with no cancel phrase doesn't fire the cancel
  sequence

Each test writes its desired `utterance_transformers` entry into a
temp xdg-config file and boots MiniCroft with `isolate_config=False`,
because MiniCroft's default `Configuration.reload()` wipes any
in-memory override done in setUp.

Workflow: drop bus_coverage (transformers don't emit bus events).

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

* refactor: factor lang resolution into _resolve_lang helper

The transformer signature passes context = message.context, which
makes the lang source ambiguous:

- ovos-core's IntentService currently writes a top-level
  context["lang"] before invoking transformers (a known
  convenience that should be in the spec but isn't yet — the future
  transformer signature will likely grow an explicit lang kwarg);
- the normative carrier is context["session"]["lang"] per
  OVOS-MSG-1 §4.

Trust the current ovos-core contract (top-level context.lang) but
fall back to session.lang so the plugin keeps working when called
without the ovos-core pre-processing (HiveMind, tests, alternative
bus clients). Encapsulated in _resolve_lang(context) with a
docstring naming the gap so the call site can be simplified once the
spec is updated.

Also fixes the silent crash when context.get("lang") returns
explicit None: standardize_lang(None) raised AttributeError,
which UtteranceTransformersService swallowed as a warning — the
plugin no-op'd. or-chain guards both lookups.

The entry-point rename was reverted to keep this PR's scope to the
spec-tools migration; the default-config mismatch (OVOS default
config keys ovos-utterance-plugin-cancel but new dev's entry
point is ovos-utterance-cancel-plugin) belongs in a separate
ovos-config fix.

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

* fix: register entry point under opm.transformer.text (the group OPM scans)

OPM discovers utterance transformers under the entry-point group
`opm.transformer.text` (`ovos_plugin_manager/utils/__init__.py:72`,
`PluginTypes.UTTERANCE_TRANSFORMER`). The previous pyproject used
`ovos.utterance.transformer`, which OPM doesn't scan — so the
editable install in CI registered the plugin under a group nobody
looks at, OPM returned no plugins, and the cancel transformer never
loaded regardless of config.

The PyPI 0.2.8 wheel happens to work because it uses the legacy
`neon.plugin.text` group which OPM aliases (with a deprecation
warning); the new pyproject silently broke discovery.

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

* fix(test): drop final_session check on cancel mid-sentence

bus-client 1.x's SessionManager normalises `session.lang` to the
macro tag (`en-US` -> `en`), which trips End2EndTest's
`test_final_session` equality check. The cancel sequence (four
expected messages) is the real assertion here; the final-session
check was inherited from ovos-core's copy of this test and is
redundant.

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

* fix(locale): correct Danish 'annulle'->'annuller' and Portuguese 'comado'->'comando'

CodeRabbit caught two locale typos in cancel.voc:

- **da-DK**: `annulle kommando` was a broken stem; the Danish
  imperative is `annuller`. The next four lines in the same file
  already use `annuller` correctly, so this was a single-line typo.
- **pt-PT**: every occurrence of `comado` is a typo for `comando`
  (Portuguese for 'command'). Six occurrences across six template
  lines, all in the same vocabulary group `(ordem|comado|isso)` and
  variants — replaced with sed -i.

The other CodeRabbit findings (UTF-8 read, order-preserving dedupe,
deterministic test phrasing, immutable workflow ref) are addressed
elsewhere or intentionally skipped: file reading now goes through
`LocaleResources` which handles UTF-8 via the spec-tools reader;
gh-automations workflow refs use `@dev` by org policy.

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

* fix: require ovos-utils>=0.11.1a1 (standardize_lang_tag region-preserving)

ovos-utils 0.11.1a1 restores the historic langcodes semantics of
`standardize_lang_tag(macro=True)` — it performs macrolanguage
substitution (`cmn` -> `zh`) instead of stripping the region
(`en-US` -> `en`). With that fix in the dependency chain,
`ovos_bus_client.session.SessionManager` no longer rewrites
`session.lang` from `en-US` to `en`, and the cancel ovoscope
tests can again assert against `final_session`.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: JarbasAi <jarbasai38@mailfence.com>
…tial #7) (#35)

* feat: cancel.blacklist veto for utterances *about* a cancel word (partial #7)

Issue #7 calls out the false-positive class where the cancel
transformer fires on utterances that mention a cancel word without
intending to cancel anything — `play nevermind`, `spell forget that`,
`what is the opposite of cancel`, etc. The proper fix is
post-intent-dispatch (only cancel when no play/say/spell/common_qa
intent matched), which needs coordination with ovos-core; until then
this gates by **utterance prefix**.

Adds an optional `locale/<lang>/cancel.blacklist` companion to
`cancel.voc`. `.blacklist` is the OVOS-INTENT-2 §4.3 phrase-set
role an engine uses to *exclude* matches — here, any utterance
starting with a phrase in `cancel.blacklist` is exempt from the
cancel suffix match. A missing `cancel.blacklist` for a language
is non-fatal: the plugin falls back to the historic
always-check-suffix behaviour.

Ships an en-US `cancel.blacklist` covering the four pattern classes
from issue #7:

- imperative verbs that operate on the literal word: play, say,
  spell, pronounce, read, repeat, write, type, define, explain,
  translate
- pedagogical patterns: `tell me the ...`, `teach me ...`
- question starts: `how do you ...`, `what is ...`,
  `what's the (meaning|opposite|...)`, `why don't you ...`
- desire patterns: `i want(ed) (that|the|to) ...`

ovoscope coverage: a `spell nevermind that` utterance — which would
otherwise fire the cancel sequence — falls through to intent failure
under the new veto.

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

* Update cancel.blacklist

* feat(locale): translate cancel.blacklist into all 10 supported locales

Ports the en-US blacklist patterns into ca-ES, da-DK, de-DE, es-ES,
fr-FR, gl-ES, it-IT, nl-NL, pt-BR, pt-PT. Same four pattern classes
in each:

- imperative verbs that operate on the literal word
  (play/say/spell/pronounce/read/repeat/write/type)
- pedagogical verbs (define/explain/translate, tell me, teach me)
- question starts (how do you / what is / what's the
  meaning|opposite|translation|pronunciation / why)
- meta-linguistic question forms

Each file parses cleanly through ovos_spec_tools.LocaleResources
.load_blacklist and expands to 29-51 prefixes per language. Same
header comment in every file so translators can audit and extend
without re-deriving the contract.

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add the opm-check reusable-workflow caller to complete the canonical OVOS plugin CI set. All other shared callers were already present and using OpenVoiceOS/gh-automations@dev.
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