Skip to content

fix: answer the STOP-1 broadcast ovos.stop.ping - #612

Merged
JarbasAl merged 1 commit into
devfrom
fix/stop1-broadcast-ping-subscription
Sep 8, 2026
Merged

JarbasAl merged 1 commit into
devfrom
fix/stop1-broadcast-ping-subscription

Conversation

@JarbasAl

@JarbasAl JarbasAl commented Sep 8, 2026

Copy link
Copy Markdown
Member

🤖 Auto-generated by Claude Fable 5.1 (claude-fable-5-1) via Claude Code — NOT human-reviewed. Verify before acting.

OVOS-STOP-1 §4.2:

The stop cascade polls its candidates with a candidate poll (PIPELINE-1 §4.5), on the topic pair ovos.stop.pingovos.stop.pong [...] A handler with no current activity for that session_id MUST NOT respond can_handle: true. It MAY respond can_handle: false or remain silent. A handler that does not subscribe to ovos.stop.ping is treated 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 forwards alongside the broadcast (stop_service.py emits the reply-derived ovos.stop.ping at :249 and the forward-derived twin at :262). The day that twin goes, every skill on the fleet is can_handle: false and generic stop falls back to the §4.1 step 5 recency rule. The skill now subscribes to both pings and answers on ovos.stop.pong with the §4.2 payload, {skill_id, can_handle}. The twin subscription stays for cores that predate the broadcast.

A core emitting both pings in one round draws exactly one pong. §4.2 binds the poll to PIPELINE-1 §4.5, which supplies the round identity: "the plugin keys poll state by session_id from context.session and by context.utterance_id (§9.1.1), which the ping carries by reply derivation and the pong carries back the same way", and "where one candidate answers twice in a round, the first valid pong wins". Both pings derive from the same entry Message, so both carry that pair, and the skill drops the second. Core enforces the same key from the other side, discarding a pong whose utterance_id does not match the round (stop_service.py:210). A pre-spec core that stamps no utterance_id names no round, so its pings are answered one for one and its behaviour is unchanged.

Test Before After
broadcast ping answered with the §4.2 payload fails on dev, 0 pongs — nothing subscribes passes
broadcast + per-skill twin draw one pong fails with the subscription and no dedupe, AssertionError: 2 != 1 passes
two rounds in one session, distinct utterance_id fails on a session-only round key, AssertionError: 1 != 2 passes
pre-spec core stamping no utterance_id answered every time fails on a session-only round key, AssertionError: 1 != 2 passes
two sessions in one round window passes passes
no activity for the session never claims can_handle: true passes passes
per-skill twin alone still answers passes passes

test/unittests on the merged tree: 708 passed, 1 failed — test_scheduler_delegation KeyError: 'owner', pre-existing on dev and fixed by #608.

The only consumer that counts pongs is core's handle_ack, which already deduplicates by payload skill_id; ovos-pydantic-models only declares the message shape.

@github-actions github-actions Bot added the fix label Sep 8, 2026
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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 Sep 8, 2026

Copy link
Copy Markdown
Contributor

Greetings, human! The automated checks are complete. 👾

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

📋 Repo Health

Ensuring the repo's immune system is strong (aka security checks). 🛡️

✅ All required files present.

Latest Version: 9.7.1a1

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

📡 Channel Compat — testing

The results are in the bag! 🎒

🚧 Channel unresolvable with this checkout — the repo's dependency floors exceed what the channel pins (fleet finding; see the install log).

Constraints: https://raw.githubusercontent.com/OpenVoiceOS/OpenVoiceOS/main/constraints-testing.txt
Resolved stack: channel-compat-testing artifact (constraints + pip freeze).

🔒 Security (pip-audit)

Ensuring no malicious actors are hitching a ride. 🎭

✅ No known vulnerabilities found (72 packages scanned).

⚖️ License Check

Verifying the SPDX identifiers for correctness. 🆔

✅ No license violations found.

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

🔍 Lint

The latest findings are now at your fingertips. ⌨️

ruff: issues found — see job log

📡 Channel Compat — stable

The automated checks have finished their work. 🏁

🚧 Channel unresolvable with this checkout — the repo's dependency floors exceed what the channel pins (fleet finding; see the install log).

Constraints: https://raw.githubusercontent.com/OpenVoiceOS/OpenVoiceOS/main/constraints-stable.txt
Resolved stack: channel-compat-stable artifact (constraints + pip freeze).

🔨 Build Tests

Running the forge to see if the code tempers correctly. 🔥

✅ All versions pass

Python Build Install Tests
3.10
3.11
3.12
3.13
3.14

Making code review just a little bit easier. 💆‍♂️

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>
@JarbasAl
JarbasAl force-pushed the fix/stop1-broadcast-ping-subscription branch from 54d43f8 to 65e9789 Compare September 8, 2026 19:38
@JarbasAl
JarbasAl marked this pull request as ready for review September 8, 2026 21:21
@JarbasAl
JarbasAl merged commit fa12693 into dev Sep 8, 2026
13 checks passed
@JarbasAl
JarbasAl deleted the fix/stop1-broadcast-ping-subscription branch September 8, 2026 21:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant