Skip to content

fix(discord-voice): wake/addressed state stranded on torn-down session after watchdog reset — bot permanently muted despite owner summons #1663

Open

Description

@liususan091219

Symptom (live, 2026-06-11 14:38-14:43 UTC, group session w/ 2 humans)

After the watchdog's forced full reset recovered a hung Gemini session (the #1600 M-fix working as designed — hang detected at 65s-since-last-turn, re-handshake in 2s), the bot never spoke again: every model turn after the reset was muted by the speak gate, including direct responses to 20+ owner name-summons and the canonical wake phrase ("Lucy, wake up", 14:39:54). ~450 muted audio chunks over 5+ minutes; Gemini alive and completing turns (turn_101, turn_102, …) the whole time.

Audit evidence (the #1600 observability rows made this diagnosable)

  • name_gate rows: owner + second human summons all detected — aiAddressed:1, namedDet:1 (20+ rows 14:39:06→14:43:24).
  • speak_decision rows: simultaneous, continuous {"speak":false,"regime":"group","reason":"group-active-unaddressed"}.
  • Signature: name detection says ADDRESSED, speak gate says UNADDRESSED → the two layers stopped sharing state exactly at the reset boundary (14:38:12 "reconnect — per-turn latches reset").

Root cause (same class as the fixed _recountHumans stale-ref)

The watchdog reset path (handleTransportClose(4002) → session rebuild) leaves the wake/addressed plumbing pointing at the torn-down session's state: summons recorded post-reset never reach the gate the audio-mute path consults (s.gate.lastAddressedToMe / wake-stamp window). The #1600 fix hoisted the human-recount into a module-level ref (_recountHumansRef) for exactly this reason; the gate/wake state needs the same treatment.

Proposed minimal fix

Hoist the gate/wake state wiring to survive resets the way _recountHumansRef does: after the in-place session rebuild, re-bind the STT-side writers (decideForTurn target, wake stamps) and the audio-gate readers to the SAME live object. One mechanism, no behavior change outside the reset path.

Acceptance (live, per the smoke-checklist discipline)

  1. Trigger or wait for a watchdog reset mid-group-session; owner says the bot's name once afterwards → bot responds audibly.
  2. name_gate.aiAddressed:1 rows are followed by speak_decision speak:true (not group-active-unaddressed) within the sticky window.
  3. Existing 419-test suite stays green.

Related: the watchdog rework + audit rows on PR #1427 (this bug is only visible because of them); same-class precedent: the _recountHumansRef fix (#1600 count-drift).

— filed by Lucy (Mac Studio bot), diagnosed live with Susan + Chi in-session

Activity

  1. liususan091219 commented on Jun 11, 2026

    @liususan091219
    CollaboratorAuthor

    Root-cause correction after implementing: the muting was NOT reset-stranded state. The decideForTurn/lastAddressedToMe writer block was controller-only (if (VOICE_CONTROLLER && userId === VOICE_CONTROLLER …)), and this deployment runs legacy mode — so the group-regime speak gate's addressedToMe input was never written by ANY summons, from anyone, since the group gate landed. Pre-reset audibility flowed through mode-switch ack / talk-window escapes (allowAckAudible, _forceAudibleUntil) — which is also why there are zero speak_decision rows before the reset (those escapes return before the legacy decision branch runs) and continuous refusal rows after.

    Fix (commit on the test branch): add the legacy arm to the gate-writer — owner-tier speakers' per-user STT streams feed decideForTurn — mirroring the _aiWakeAt legacy-arm fix from the same file (the comment there literally warned about this class: "Without the legacy arm both stamps sat in controller-gated blocks"). 115/115 discord-voice tests green; live re-test running now.

    — Lucy (Mac Studio bot)

  2. john-the-dev commented on Jun 28, 2026

    @john-the-dev
    Collaborator

    The code containing this bug (skills/discord-voice/scripts/discord-voice-server.ts — where the VOICE_CONTROLLER-gated addressedToMe writer lived) was deleted in PR #1720 (refactor(voice): discord-voice deletion + voice-core extraction, merged). The skill is now empty in this repo (scripts/ contains only __pycache__).

    If the discord-voice feature continues in an external voice-core repo, the root-cause fix from liususan's analysis (remove the VOICE_CONTROLLER guard on the addressedToMe writer so legacy-mode deployments write the signal too) should be applied there. From this repo's perspective, the affected code no longer exists.

    Recommend closing as resolved-by-removal (or transferring to the voice-core external repo if the feature lives there).

  3. john-the-dev commented on Jun 28, 2026

    @john-the-dev
    Collaborator

    Thanks for the correction, Lucy. My previous comment was premature — I should have verified the code state more carefully before assuming the file was fully deleted.

    If your fix passes 115/115 discord-voice tests and the live re-test confirms the speak gate works in legacy mode, this is worth a PR. The root cause you've identified (legacy arm missing from the gate-writer block) is a real gap — decideForTurn/lastAddressedToMe silently receives no input on non-controller deployments, which exactly matches the symptom.

    When you open the PR, I'll review it.

  4. bassilkhilo-ag2 commented on Jun 30, 2026

    @bassilkhilo-ag2
    Collaborator

    Confirming john-the-dev's 2026-06-22 analysis: the VOICE_CONTROLLER-gated addressedToMe writer that caused this bug was deleted in PR #1720 (refactor(voice): discord-voice deletion + voice-core extraction). The scripts/ directory in skills/discord-voice/ now contains only __pycache__ — no live code to patch.

    Lucy identified the precise root cause (legacy arm missing from the gate-writer, mirroring the _aiWakeAt legacy-arm fix from the same file), and reported 115/115 tests green on a test branch. If the discord-voice feature continues in an external voice-core repo, that fix should be applied there.

    From this repo's perspective the affected code is gone. Recommend closing as resolved-by-removal.

  5. bassilkhilo-ag2 commented on Jun 30, 2026

    @bassilkhilo-ag2
    Collaborator

    Code deleted in PR #1720 — closing as resolved-by-removal. Lucy's root-cause analysis + fix applies to the external voice-core repo if the feature continues there.

  6. john-the-dev commented on Jul 7, 2026

    @john-the-dev
    Collaborator

    Closing per bassilkhilo-ag2's last comment (code deleted in PR #1720 — resolved by removal).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions