Skip to content

refactor: register canonical intent topics; compat moves to ovos-spec-tools - #500

Open
JarbasAl wants to merge 5 commits into
devfrom
feat/canonical-intent-registration
Open

refactor: register canonical intent topics; compat moves to ovos-spec-tools#500
JarbasAl wants to merge 5 commits into
devfrom
feat/canonical-intent-registration

Conversation

@JarbasAl

@JarbasAl JarbasAl commented Jul 31, 2026

Copy link
Copy Markdown
Member

Why

OVOS-MSG-1 §2.1.1 assembles the per-intent dispatch topic from named
identifiers at runtime: <skill_id>:<intent_name>. The intent name is the
skill author's label for the intent. OVOS-INTENT-1 keeps a clean line between
authoring form and wire form — .intent is a locale resource file
extension (OVOS-INTENT-2 §3), an authoring detail. It has no business on the
bus.

Workshop built the topic straight from the resource filename, so a skill with
a food.order.intent resource ended up on <skill_id>:food.order.intent, and
register_intent_file then bound the handler to both spellings while
pipelines migrated. That leaves the same compat rule reimplemented in three
places in workshop and keeps the legacy identity alive in the skill layer,
where it does not belong.

This PR makes workshop spec-pure: it produces and consumes canonical topics
only. The wire-level compat that containerized old skills still need moves to
ovos-spec-tools, which now owns the whole canonical↔legacy surface.

What changed

  • register_intent_file derives the intent name with
    canonical_intent_topic(), so the registration payload and the dispatch
    topic both carry <skill_id>:food.order.
  • The dual add_event on the suffixed twin is gone. One binding, the
    canonical one.
  • disable_intent / enable_intent canonicalize the author-supplied name
    before hitting the registry, so a skill may still say
    self.disable_intent("food.order.intent"). The paired remove_event on the
    suffixed twin is gone with the dual binding.
  • register_intent_layer canonicalizes string intent names the same way.
  • IntentServiceInterface._clean_padatious_name keeps its suffix strip but
    reuses the INTENT_FILE_SUFFIX constant from spec-tools. It now only fires
    for the deprecated register_padatious_intent API — the modern path already
    passes a canonical name.

No compat code is left in workshop.

Old containers still work

A skill container running an old workshop subscribes to the suffixed topic
over the real bus. ovos_spec_tools.intent_topics covers it at the bus layer:
IntentAliasRegistry records that an old consumer registered the suffixed
spelling, and legacy_reemit_targets — gated by the existing emit_legacy
convention — mirrors the canonical dispatch onto the suffixed twin for exactly
those intents. Alias-driven by default, so no topic is invented.

Depends on

Pairs with OpenVoiceOS/ovos-spec-tools#88, which adds
canonical_intent_topic and the compat helpers. That PR must merge and
release before this one: the floor here is bumped to
ovos-spec-tools>=1.6.0 and CI stays red until that release exists. No git
ref is pinned in pyproject.toml — a direct URL there breaks the PyPI
publish.

Tests

test/unittests/skills/test_base.py, rewritten around the spec-pure
behaviour:

  • a skill authored with a time.intent resource registers
    <skill_id>:time with the intent service;
  • the handler is bound to the canonical topic and not to the suffixed one;
  • emitting the canonical topic fires the handler;
  • no <skill_id>:…​.intent topic is emitted or bound by workshop during
    registration (the "spec-pure" guard);
  • disable_intent("time.intent") still works and queries the registry by the
    canonical name.

test/unittests/test_skill.py and test/unittests/test_decorators.py are
updated for the canonical names they now see on the wire.

Run with the spec-tools branch on PYTHONPATH:
test_base.py 74 passed; test_skill.py + test_decorators.py 37 passed;
test_intent4_producer.py, test_intent4_adversarial.py,
test_intent_service_interface.py, test_malformed_template_resilience.py,
test_inline_vocab_refs.py 55 passed.

skills/test_intent_layers_e2e.py errors in setup both with and without this
branch — the demo skill does not load in a worktree checkout. Pre-existing,
unrelated.


Maintainer-approved design. Implemented by Claude (opus), orchestrated by
Claude Fable.

🤖 Generated with Claude Code

The dispatch topic is `<skill_id>:<intent_name>` (OVOS-MSG-1 §2.1.1). The
`.intent` extension is an authoring resource detail (OVOS-INTENT-2 §3), so it
must not reach the wire.

`register_intent_file` now derives the name with `canonical_intent_topic` and
binds one event, the canonical one. The dual add_event/remove_event on the
suffixed twin is gone; `disable_intent` / `enable_intent` /
`register_intent_layer` canonicalize the author-supplied name.

Old containerized skills still listen on the suffixed topic. That compat now
lives in ovos-spec-tools (`intent_topics`), gated by `emit_legacy` at the bus
layer, and no longer in the skill layer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next review available in: 37 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a4b19cd9-252f-4460-8498-da4a9dc3e4d7

📥 Commits

Reviewing files that changed from the base of the PR and between c59df90 and a234c62.

📒 Files selected for processing (6)
  • ovos_workshop/intents.py
  • ovos_workshop/skills/ovos.py
  • pyproject.toml
  • test/unittests/skills/test_base.py
  • test/unittests/test_decorators.py
  • test/unittests/test_skill.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Beep! I'm back with the goodies! 🍭

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

🔍 Lint

The latest check report is officially filed. 📁

ruff: issues found — see job log

📋 Repo Health

Your repository is in great shape! ✅

✅ All required files present.

Latest Version: 9.3.2a2

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

🔨 Build Tests

The build process has successfully terminated. 🏁

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

🔒 Security (pip-audit)

Checking for any digital cooties in your dependencies... 👾

✅ No known vulnerabilities found (74 packages scanned).

⚖️ License Check

Ensuring our CLA requirements are met. 🖋️

✅ No license violations found.

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


A robot's work is never done... but this PR check is! ⚙️

JarbasAl and others added 2 commits August 1, 2026 14:01
Relocated from ovoscope#127 (TestRegistrationNormalizationDedup) at the
maintainer's request: ovoscope is the harness library, not the stack, and
these guards belong next to the code they pin.

A legacy `X:Y.intent`-registered handler must still fire exactly once on a
canonical `X:Y` dispatch, and registering both spellings for one intent
must collapse to a single fire, not double-dispatch. Uses
ovos_spec_tools.intent_topics.IntentAliasRegistry (the real alias-collapse
API — the ovoscope draft referenced a nonexistent
ovos_spec_tools.intent_compat.normalize_intent_registration). Both pass
outright since #500 already registers canonical-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1.6.0a1 is released; a bare >=1.6.0 floor cannot resolve an alpha.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The alias registry is gone from ovos-spec-tools. The two guards assert the
same behavior with the pure helper the registry wrapped.
spec-tools#92 (registry removal) merged and released as 1.6.0a2;
pin to the simplified intent_topics surface.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@JarbasAl
JarbasAl marked this pull request as ready for review August 1, 2026 18:03
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