Skip to content

Implement FALLBACK-1 skill-addressed polling - #808

Closed
goldyfruit wants to merge 7 commits into
OpenVoiceOS:devfrom
goldyfruit:codex/scoped-fallback-pongs-2.1
Closed

goldyfruit wants to merge 7 commits into
OpenVoiceOS:devfrom
goldyfruit:codex/scoped-fallback-pongs-2.1

Conversation

@goldyfruit

@goldyfruit goldyfruit commented Jul 6, 2026

Copy link
Copy Markdown

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

narrows #807 (same-session stale-pong residue remains; needs a round nonce -- pre-existing on dev) by resolving the underlying shared-pong race with the architecture defined by FALLBACK-1.

Architecture

  • Poll each registered fallback on its skill-owned <skill_id>.fallback.ping topic and collect the corresponding <skill_id>.fallback.pong.
  • Keep query correlation at the OVOS session boundary, so simultaneous sessions cannot consume each other’s replies.
  • Derive each dotted probe with message.reply as required by FALLBACK-1 §6.1, then apply priority ordering, session exclusions, fallback mode, and timeout behavior to a locked registry snapshot.
  • Synchronize fallback lifecycle handler installation/removal so concurrent registration and deregistration cannot leave listeners behind.

This intentionally does not add a generic agent traffic hook, a request-ID compatibility layer, or a shared ovos.skills.fallback.pong collector. The coordinated skill-side change is OpenVoiceOS/ovos-workshop#465; both repositories use the same FALLBACK-1 dotted topics.

Validation

  • 42 focused fallback-service tests pass, including both spec/legacy end-to-end namespace paths, concurrent-session isolation, priority selection, and lifecycle-race coverage.
  • The 11 companion Workshop fallback tests pass through the real FakeBus ping/pong route against the coordinated packages.
  • Ruff passes.
  • The previous GitHub build, coverage, docs, license, locale, OPM, audit, release, repository-health, type, and Ovoscope checks passed; the new head is re-running that unchanged CI matrix.

Summary by CodeRabbit

  • Bug Fixes
    • Improved fallback handling for overlapping requests within the same session.
    • Validated fallback responses against the correct session and skill.
    • Improved fallback candidate filtering, priority handling, timeout cleanup, and lifecycle reliability.
    • Fixed session blacklisting so it consistently applies across matcher variants and confidence levels.
  • Testing
    • Expanded coverage for skill-specific fallback communication, malformed responses, timeouts, concurrency, and matcher blacklisting.

Rework after adversarial audit (2026-08-12)

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

The flag-day defect. This PR and its workshop counterpart (#465) did a
flag-day switch to skill-addressed <skill_id>.fallback.ping/.pong with no
compatibility window: this branch dropped the broadcast
ovos.skills.fallback.ping poll and the ovos.skills.fallback.pong
collector outright. Executed result: this PR + ANY released ovos-workshop
(including the current 9.3.9a1 dev/floor pin) = fallback dead (the released
workshop only answers the broadcast ping, which this branch stopped
sending). Symmetrically, #465 + any released ovos-core = fallback dead. The
original e2e test only passed because _wire_skill_addressed_probe in
test/end2end/test_fallback.py faked the workshop-side pong; it failed
against the real #465 branch (double pong, 14 messages vs 13 expected).
FALLBACK-1 §6.1 makes the skill-addressed topics normative but explicitly
sanctions the broadcast poll as an observably-equivalent optimisation, so a
dual window is conformant.

The fix. One deprecation window (kill-switch #837 conventions):

  • Emit the general ovos.skills.fallback.ping broadcast once per poll
    round alongside the addressed pings, and keep a
    ovos.skills.fallback.pong collector with the same session filter as
    the addressed collectors.
  • Dedup pongs by skill_id: a skill running fixed ovos-workshop (fr-fr/intents #465)
    answers BOTH ping families during the window and must only count once
    (first answer wins). New unit tests cover both orderings.
  • Deleted _wire_skill_addressed_probe and its uses from
    test/end2end/test_fallback.py -- the real (released) ovos-workshop
    installed by the test run now has to answer honestly. Updated
    expected_messages to reflect the addressed ping being
    emitted-but-unanswered against a pre-fr-fr/intents #465 workshop, with the broadcast
    ping/pong carrying the actual round-trip.
  • Fixed the CodeRabbit-flagged flaky session-lock unit test: corrected its
    docstring (it exercises the session-id filter, not the lock, kept an
    explicit generous fallback_query_timeout) and added a real
    same-session lock-serialization test that exercises
    _acquire_fallback_session_lock with two concurrent same-session polls.
  • Split out an unrelated pipeline-blacklist-normalization change to
    ovos_core/intent_services/service.py that had leaked into this
    branch -- now fix: normalize pipeline ids when matching session blacklists #854 (draft).

Executed matrix (probe-free harness: real ovoscope + real
ovos-skill-fallback-unknown, no fake ping/pong):

core workshop ping answered dispatched once skill spoke once result
fixed (#808) fixed (#465) yes yes yes GREEN
fixed (#808) released dev (9.3.9a1) yes yes yes GREEN
unfixed dev fixed (#465) yes yes yes GREEN
fixed (#808) PyPI 9.3.9a1 yes yes yes GREEN

All four cells green. Full ovos-core unit suite green (329 passed, 9
subtests), full end2end suite green (40 passed, 72 subtests) -- including
test/end2end/test_fallback.py running honestly against the real,
unmodified, released ovos-workshop with no probe installed.

Verified claims: model re-check only (this session), not human-reviewed
-- matrix cells, red-before-fix on the dedup test (revert-code-keep-test
technique, no git stash), and the full unit/e2e suites were all actually
executed in this session, not asserted from memory.

Fixes #807 -> narrows #807 (same-session stale-pong residue remains;
needs a round nonce -- pre-existing on dev, out of scope here).

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b7c9a62-885e-4cc4-a579-864994cfb3a0

📥 Commits

Reviewing files that changed from the base of the PR and between c614232 and 9d7e066.

📒 Files selected for processing (2)
  • ovos_core/intent_services/service.py
  • test/unittests/test_intent_service_extended.py

📝 Walkthrough

Walkthrough

FallbackService now serializes fallback polling per session, uses locked registry snapshots, validates skill-specific responses, and cleans up listeners and locks. Intent-service pipeline filtering now normalizes matcher identifiers before blacklist checks.

Changes

Session-scoped fallback polling

Layer / File(s) Summary
Registry and session synchronization
ovos_core/intent_services/fallback_service.py
Registration and deregistration use a registry lock. Locked snapshots provide fallback candidates. Per-session polling locks use reference counting.
Session-aware response collection
ovos_core/intent_services/fallback_service.py
Polling filters candidates by priority, blacklist, and fallback mode. Skill-specific handlers validate session, skill, and boolean response data. Timeout and cleanup handling run for each poll.
Fallback polling validation
test/unittests/test_fallback_service.py, test/end2end/test_fallback.py
Tests cover skill-specific topics, priority ordering, malformed responses, snapshot isolation, concurrent sessions, timeout cleanup, lifecycle wiring, and handler cleanup.

Pipeline blacklist normalization

Layer / File(s) Summary
Pipeline blacklist filtering
ovos_core/intent_services/service.py, test/unittests/test_intent_service_extended.py
Blacklist checks normalize matcher identifiers and confidence variants. Tests verify that suffixed matcher variants are filtered correctly.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant FallbackService
  participant SkillFallbackHandler
  participant Skill

  FallbackService->>FallbackService: acquire session polling lock
  FallbackService->>SkillFallbackHandler: register skill-specific pong listener
  FallbackService->>Skill: emit skill-specific fallback ping
  Skill-->>SkillFallbackHandler: return session and skill response
  SkillFallbackHandler-->>FallbackService: validate and collect response
  FallbackService->>SkillFallbackHandler: remove listener
  FallbackService->>FallbackService: release session polling lock
Loading

Suggested reviewers: jarbasal

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The implementation uses skill-addressed topics but does not implement the request-ID and legacy-pong requirements in [#807]. Add request IDs to fallback pings, reject pongs for other request IDs, and preserve support for legacy pongs without IDs.
Out of Scope Changes check ⚠️ Warning The intent-service blacklist normalization changes are unrelated to the fallback polling requirements in [#807]. Move the intent-service changes to a separate pull request or link the issue that requires them.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: implementing skill-addressed fallback polling.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@goldyfruit
goldyfruit force-pushed the codex/scoped-fallback-pongs-2.1 branch from 5e3af77 to 43e0e88 Compare July 7, 2026 13:13
@JarbasAl

JarbasAl commented Aug 2, 2026

Copy link
Copy Markdown
Member

Reviewed the fallback-pong scoping fix. The concurrency correctness is sound: scoping pongs by a per-query fallback_request_id, replacing the shared instance-level _fallback_response_event with a per-call event, and snapshotting the registry under an RLock all address real races between overlapping fallback collections. Back-compat is by tolerance (a legacy pong with no id → ack_request_id is None → accepted), which matches OVOS wire back-compat practice — good.

The red CI is not your change. Unit tests pass on 3.10–3.14 and the fallback tests pass in coverage; the 11 reds are stale (2026-07-07): coverage fails only at a 403 Resource not accessible comment-post step on contributor PRs, type_check is pre-existing tree-wide mypy debt (nothing in fallback_service.py), and ovoscope is a shared-workflow env failure. A CI re-run should clear them.

Two notes: (1) this pairs with ovos-workshop#465 (skill-side echo) — they're mutually back-compatible in either merge order; (2) spec-direction only, non-blocking: FALLBACK-1 §6.1 prescribes per-skill dotted topics (<skill_id>.fallback.ping/pong) which are inherently request-scoped, so the spec-ideal fix is dotted addressing rather than a request-id bolted onto the legacy broadcast — fine as a fix to the code as it exists today. Minor: self._fallback_response_event is now dead and can be dropped.


Review written by Claude Opus 4.8 (claude-opus-4-8) without human oversight.

@JarbasAl

JarbasAl commented Aug 2, 2026

Copy link
Copy Markdown
Member

Correction to my earlier review — this should be rejected as-is on spec-compliance grounds.

The underlying concurrency bug (#807) is real and worth fixing, but fallback_request_id is not a field in the FALLBACK-1 spec and the approach conflicts with it. OVOS-FALLBACK-1 §6.1 prescribes per-skill dotted ping/pong topics — <skill_id>.fallback.ping / <skill_id>.fallback.pong — whose pong payload is only skill_id + can_handle. Dotted addressing is inherently request-scoped: each collection round talks to a specific skill's topic, so there is no cross-request pong contamination to disambiguate and no id field is needed. The architecture spec is prescriptive, so the correct fix is to migrate the collector (and the skill side, ovos-workshop#465) to the spec's dotted topics rather than bolt a non-spec fallback_request_id onto the legacy broadcast ovos.skills.fallback.ping/pong.

The per-call event + registry-snapshot/locking parts of this PR are good and worth keeping; the request-id scoping should be replaced by dotted addressing. Recommend reworking toward §6.1 (or closing in favor of a spec-compliant PR). Not the version-guard/wire-tolerance pattern here — this is a case where the spec dictates the topic scheme.


Review written by Claude Opus 4.8 (claude-opus-4-8) without human oversight.

@goldyfruit
goldyfruit force-pushed the codex/scoped-fallback-pongs-2.1 branch from 43e0e88 to 173f567 Compare August 4, 2026 00:25

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
ovos_core/intent_services/fallback_service.py (1)

54-80: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard _lifecycle_handlers with the registry lock.

handle_register_fallback and handle_deregister_fallback run on bus dispatch threads. _wire_lifecycle and _unwire_lifecycle mutate _lifecycle_handlers without synchronization. Two concurrent registers for the same skill_id can both pass the membership check and attach duplicate listeners, and only the last pair is stored, so the first pair leaks. A register that interleaves with a deregister can also leave listeners attached after deregistration. _registered_fallbacks_lock is an RLock, so it can wrap both methods.

🔒 Proposed fix
     def _wire_lifecycle(self, skill_id: str) -> None:
         """Translate lifecycle done-signal for a fallback skill."""
-        if skill_id in self._lifecycle_handlers:
-            return
+        with self._registered_fallbacks_lock:
+            if skill_id in self._lifecycle_handlers:
+                return
+            self._lifecycle_handlers[skill_id] = (_on_start, _on_response)

Move the handler definitions above the lock block and register the bus listeners inside it, then apply the same lock to the pop in _unwire_lifecycle:

     def _unwire_lifecycle(self, skill_id: str) -> None:
-        handlers = self._lifecycle_handlers.pop(skill_id, None)
+        with self._registered_fallbacks_lock:
+            handlers = self._lifecycle_handlers.pop(skill_id, None)
         if not handlers:
             return
🤖 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_core/intent_services/fallback_service.py` around lines 54 - 80, Guard
lifecycle listener registration and removal with the existing
_registered_fallbacks_lock. In _wire_lifecycle, define the callbacks before
acquiring the lock, then perform the membership check, bus.on calls, and
_lifecycle_handlers assignment atomically inside the RLock; in
_unwire_lifecycle, protect the pop and listener removal with the same lock so
concurrent registration and deregistration cannot leak or duplicate handlers.
🧹 Nitpick comments (2)
ovos_core/intent_services/fallback_service.py (2)

289-289: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Rename the unused loop variable.

Ruff reports B007. prio is not used in the loop body.

♻️ Proposed change
-        for skill_id, prio in sorted_handlers:
+        for skill_id, _prio in sorted_handlers:
🤖 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_core/intent_services/fallback_service.py` at line 289, In the loop over
sorted_handlers, rename the unused prio variable to the conventional underscore
placeholder while preserving skill_id iteration and the loop body unchanged.

Source: Linters/SAST tools


218-220: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Use message.forward for the outbound fallback ping.

message.reply swaps context["source"] and context["destination"], so the ping is addressed to the utterance source. Similar skill-query pings use forward, and test/end2end/test_fallback.py keeps ovos.skills.fallback.ping on the original source. Use forward so skills receive a ping with the expected forward context.

♻️ Proposed change
             for skill_id in pool:
-                self.bus.emit(message.reply(
-                    f"{skill_id}.fallback.ping", query_data))
+                self.bus.emit(message.forward(
+                    f"{skill_id}.fallback.ping", query_data))
🤖 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_core/intent_services/fallback_service.py` around lines 218 - 220, Update
the fallback ping loop in the relevant fallback service method to emit each
“{skill_id}.fallback.ping” message via message.forward instead of message.reply,
preserving the original source and destination context expected by fallback
skills.
🤖 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_core/intent_services/fallback_service.py`:
- Around line 188-220: Update the fallback candidate query in the surrounding
handler registration and emission flow to use the shared FALLBACK-1 topics,
ovos.skills.fallback.pong for listener registration and
ovos.skills.fallback.ping for emitted requests, instead of constructing topics
from each skill_id. Keep the existing per-skill response validation and
filtering behavior unchanged.

In `@test/unittests/test_fallback_service.py`:
- Around line 354-410: Update
test_concurrent_sessions_do_not_consume_each_others_pongs to configure an
explicit fallback query timeout large enough to cover its polling and delayed
pong delivery, rather than relying on _make_service() defaults. Correct the
docstring to state that the test verifies pong filtering by session ID, not
serialization via _acquire_fallback_session_lock.

---

Outside diff comments:
In `@ovos_core/intent_services/fallback_service.py`:
- Around line 54-80: Guard lifecycle listener registration and removal with the
existing _registered_fallbacks_lock. In _wire_lifecycle, define the callbacks
before acquiring the lock, then perform the membership check, bus.on calls, and
_lifecycle_handlers assignment atomically inside the RLock; in
_unwire_lifecycle, protect the pop and listener removal with the same lock so
concurrent registration and deregistration cannot leak or duplicate handlers.

---

Nitpick comments:
In `@ovos_core/intent_services/fallback_service.py`:
- Line 289: In the loop over sorted_handlers, rename the unused prio variable to
the conventional underscore placeholder while preserving skill_id iteration and
the loop body unchanged.
- Around line 218-220: Update the fallback ping loop in the relevant fallback
service method to emit each “{skill_id}.fallback.ping” message via
message.forward instead of message.reply, preserving the original source and
destination context expected by fallback skills.
🪄 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 Plus

Run ID: 0377377e-d27c-4d0a-8d42-0716ad186a37

📥 Commits

Reviewing files that changed from the base of the PR and between 5e3af77 and 173f567.

📒 Files selected for processing (2)
  • ovos_core/intent_services/fallback_service.py
  • test/unittests/test_fallback_service.py

Comment thread ovos_core/intent_services/fallback_service.py
Comment thread test/unittests/test_fallback_service.py
@goldyfruit goldyfruit changed the title Scope fallback pongs on 2.1 Implement FALLBACK-1 skill-addressed polling Aug 4, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
test/end2end/test_fallback.py (1)

110-115: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the fallback wire contract explicitly.

End2EndTest checks only keys listed in the expected Message and ignores extra keys. Therefore, removing range from the expected payload does not prove that the emitted ping or request omits it. Omitting context["session"] also does not detect lost session propagation. (pypi.org)

Add session to the expected ping, pong, and request contexts. Add an exact-payload or explicit assertNotIn("range", actual_message.data) check for the skill-addressed ping and fallback request.

Proposed session assertions
 Message(f"{self.skill_id}.fallback.ping",
         {"utterances": ["hello world"],
-         "lang": session.lang}),
+         "lang": session.lang},
+        {"session": session.serialize()}),

 Message(f"{self.skill_id}.fallback.pong",
         {"skill_id": self.skill_id, "can_handle": True},
-        {"source": "A", "destination": "B"}),
+        {"session": session.serialize(),
+         "source": "A", "destination": "B"}),

 Message(f"ovos.skills.fallback.{self.skill_id}.request",
         {"utterances": ["hello world"],
          "lang": session.lang,
-         "skill_id": self.skill_id}),
+         "skill_id": self.skill_id},
+        {"session": session.serialize()}),

Also applies to: 126-128

🤖 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_fallback.py` around lines 110 - 115, Update the expected
messages in End2EndTest to include session in the ping, pong, and request
contexts, preserving the emitted session value. Strengthen the fallback
wire-contract assertions by checking the skill-addressed ping and fallback
request payloads exactly or explicitly asserting that range is absent from each
actual message’s data; apply the same changes to the additional message cases
referenced by the comment.
🤖 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.

Nitpick comments:
In `@test/end2end/test_fallback.py`:
- Around line 110-115: Update the expected messages in End2EndTest to include
session in the ping, pong, and request contexts, preserving the emitted session
value. Strengthen the fallback wire-contract assertions by checking the
skill-addressed ping and fallback request payloads exactly or explicitly
asserting that range is absent from each actual message’s data; apply the same
changes to the additional message cases referenced by the comment.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3cb7562f-0e11-440a-a052-79ade95d9072

📥 Commits

Reviewing files that changed from the base of the PR and between 173f567 and c614232.

📒 Files selected for processing (3)
  • ovos_core/intent_services/fallback_service.py
  • test/end2end/test_fallback.py
  • test/unittests/test_fallback_service.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/unittests/test_fallback_service.py
  • ovos_core/intent_services/fallback_service.py

goldyfruit and others added 2 commits August 12, 2026 09:28
… for the migration window

The skill-addressed fallback ping/pong machinery landed with no
compatibility window: any released ovos-workshop (pre-OpenVoiceOS#465, broadcast-only)
paired with this branch never gets its fallback ping answered, since the
broadcast ping/pong collector was dropped outright. FALLBACK-1 §6.1 makes
the addressed topics normative but explicitly sanctions the broadcast poll
as an observably-equivalent optimisation, so restore it for one
deprecation window (kill-switch OpenVoiceOS#837 conventions):

- emit the general `ovos.skills.fallback.ping` broadcast once per poll
  round alongside the addressed pings, and keep a `ovos.skills.fallback.pong`
  collector with the same session filter as the addressed collectors.
- dedup pongs by skill_id: a skill running fixed ovos-workshop (OpenVoiceOS#465)
  answers BOTH ping families during the window, and must only count once
  (first answer wins).
- delete the end2end `_wire_skill_addressed_probe` fake and let the real
  (released) ovos-workshop installed by the test run answer the ping
  honestly; expected_messages updated to reflect the addressed ping being
  emitted-but-unanswered against a pre-OpenVoiceOS#465 workshop.
- fix the CodeRabbit-flagged flaky session-lock unit test's docstring
  (it exercises the session-id filter, not the lock) and add a real
  same-session lock serialization test.
- revert the unrelated pipeline-blacklist normalization change to
  intent_services/service.py (and its test) that had leaked into this
  branch; it is being split into its own PR.

Executed matrix (probe-free harness, real ovoscope + real workshop):
fixed-core+fixed-ws, fixed-core+ws-dev, core-dev+fixed-ws, and
fixed-core+PyPI ovos-workshop==9.3.9a1 are all GREEN (ping answered,
dispatched exactly once, skill spoke exactly once).

Narrows OpenVoiceOS#807 (same-session stale-pong residue remains; needs a round
nonce -- pre-existing on dev).

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

Copy link
Copy Markdown
Member

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

Closed by owner ruling (2026-08-13): this pair predates the ratified FALLBACK-1 broadcast-contest shape. Fallback will be reworked on the broadcast pattern established by ovos-core#863 + ovos-workshop#534 (one broadcast question per round, parallel answers, explicit declines, early close) once that pair lands — one migration instead of two. Nothing is broken by the closure: the pair was never merged.

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.

Scope fallback pong collection per request

2 participants