Repository navigation
Release candidate: server-v0.2.0-rc.1 - #185
Open
BrettKinny wants to merge 156 commits into
Open
BrettKinny wants to merge 156 commits into
BrettKinny wants to merge 156 commits into
Conversation
Correct the pinned firmware source, first-boot provisioning path, effective OTA configuration, and stale compatibility metadata. Add regression guards, protect raw UAT evidence from accidental commits, and keep the calendar route test offline. Co-Authored-By: OpenAI Codex <noreply@openai.com>
Correlate new_session acknowledgements by request ID and surface reset failures instead of accepting stale responses. Co-Authored-By: OpenAI Codex <noreply@openai.com>
…ions Replace the broken direct-ALSA capture default with the C920 PipeWire source and validate decoded sample coverage. Add bounded session coordination, honest evidence gates, checkpoint quarantine, and local clip review exports. Co-Authored-By: GPT-6 <noreply@openai.com>
Add PCM clipping and silence metrics, optional pre-rendered prompts, truthful follow-up readiness, and a source-based feature inventory. Preserve unknown visual and tool execution outcomes. Co-Authored-By: GPT-6 <noreply@openai.com>
…wups Keep VAD enabled with peak-limited analysis gain and immutable provenance. Require actual wake frames for cold-wake claims; block warm tests unless the live microphone is listening. Use deployed follow-up readiness contracts. Co-Authored-By: GPT-6 <noreply@openai.com>
Gate ambient turns on current motion ownership and refresh the post-chat quiet interval. Eight deterministic failures reproduced before the fix; 44 focused tests pass. Physical acceptance remains pending. Co-Authored-By: GPT-6 <noreply@openai.com>
Microphone evidence proved VAD can miss genuine quiet robot replies. Keep original case evidence and do not infer silence from an empty transcript. 57 coordinator regressions pass. Co-Authored-By: GPT-6 <noreply@openai.com>
Discover all test suites including cached take_photo, execute previously skipped memory integration checks against invented SQLite fixtures, and clear live service test overrides. Eleven suites and 124 assertions pass without household data. Co-Authored-By: GPT-6 <noreply@openai.com>
Preserve per-attempt prompt provenance and reject stale or mismatched waveforms before playback. Clear inherited overrides so one case cannot accidentally play another prompt. 75 coordinator regressions pass. Co-Authored-By: GPT-6 <noreply@openai.com>
Keep default threshold 0.5 and prompt transcription unchanged. Validate response-only prospective settings and persist actual transcription provenance; preserve inconclusive silence and unverified text semantics. Co-Authored-By: GPT-6 <noreply@openai.com>
Content-only and speaker-without-language envelopes were treated as spoken JSON. Decode content independently of optional metadata and reject non-text payloads before noise filtering. Regression tests exercise startToChat with mocked side effects; seven pre-fix scenarios failed and now pass. Live deployment remains pending. Co-Authored-By: GPT-6 <noreply@openai.com>
Document touch/admin wake, disabled physical face and voice producers, neutral parking, and historical release-pin differences. Correct the current voice-tool catalogue to seven without changing product behavior. Co-Authored-By: GPT-6 <noreply@openai.com>
Physical V06 correctly recognized a small-robot joke, then the sliding edit-distance correction manufactured a story command. Limit known-phrase cleanup to exact word sequences with punctuation normalization, preserving sentence boundaries and explicit observed name aliases. Actual startToChat replay and negative phrase regressions pass. Negated/quoted substring routing remains a separate known limitation; deployment pending. Co-Authored-By: GPT-6 <noreply@openai.com>
Protect literal state and wake shortcuts from immediately negated commands and paired quoted mentions. Preserve quote context during ASR punctuation normalization. Exercise the actual ASR-to-MCP boundary with affirmative and escape regressions; indirect and hypothetical language remains outside this bounded guard. Co-Authored-By: GPT-6 <noreply@openai.com>
Local safety-review candidate only; not deployed. Deny physical voice capture and cached voice descriptions unless the shared guardian policy explicitly enables adult mode. Re-read per access and fail closed on policy errors. Keep ambient/admin camera behavior unchanged. Regression evidence: red-first tests, 359 root tests plus 25 subtests and 250 behaviour tests passed; Compose validation passed. Human safety/red-team review and separate deployment approval remain required. Co-Authored-By: GPT-6 <noreply@openai.com>
Remove workstation-specific host/path defaults from public harness code and documentation. Initialization fails before network access without --host or DOTTY_TEST_HOST; existing session config remains authoritative. Co-Authored-By: GPT-6 <noreply@openai.com>
…eadiness Map an opt-in lossless sidecar from the same bounded capture process, request float Pulse input, and reject existing outputs. Add explicit warm-listening prerequisites for state-command trials. Verify with synthetic dual-output capture and isolated runner tests; no hardware or volume changes. Co-Authored-By: GPT-6 <noreply@openai.com>
Bound native-rate float decoding by evidence windows and preserve prompt clipping warnings independently of the quiet response tail. Include optional lossless sidecar measurements with explicit non-sample-aligned timing caveats; do not infer interaction verdicts. Replayed J02 metrics without changing historical evidence; all380 root tests and25 subtests pass offline. Co-Authored-By: GPT-6 <noreply@openai.com>
Gate warm-case capture on the perception server's own clock: compute chat-listening status age from sensor_age_s, last_event_t and last_chat_status_t, so unrelated sensor events cannot make a stale listening flag look fresh. Missing, non-finite or inconsistent timestamps block the case as warm_listening_precondition rather than passing silently. Capture is a prerequisite for a new trial only; recovery after a long capture is separate. 108 overnight harness tests pass. Co-Authored-By: Qwen3.8-Max <noreply@qwen.ai>
A device or admin abort set conn.client_abort and nothing on the nointent chat path cleared it, so every later reply on that connection was cut off until the robot reconnected. Reproduced on the physical robot 2026-10-05 (abort, then 'What is your name?' -> ASR + LLM ran, no TTS). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Near-silence after Dotty finished speaking was transcribed as 'Thank you.' (no_speech_prob 0.81) and answered. Real utterances captured on the physical robot 2026-10-05 all scored <= 0.32. Gate at Whisper's default 0.6, configurable via ASR.WhisperLocal.no_speech_threshold (1.0 disables). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Docker host runs agent/post-release-automation's pi_client.py and pi_voice.py (direct remember/recall/think_hard routing). Bring this branch level with the deployment so further fixes do not revert it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PiClient spawned pi with no --system-prompt, so pi's built-in coding-assistant prompt was the only identity the model saw and it leaked into speech (#177). Pass personas/pi_voice.md via --system-prompt (DOTTY_PI_SYSTEM_PROMPT_FILE overrides; a missing file keeps the old behaviour). Offline A/B in the dotty-pi container, 8 samples each of a self-review prompt: before 1/8 'coding assistant' and 4/8 off-topic; after 0/8 and 0/8. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uator slips as faults Refs #182. Opt-in --mic-opener admin_say makes a warm case open the microphone through /xiaozhi/admin/say instead of blocking, so a session can run unattended. Playback is also proven by the robot's own recognition; a reference-mic mishearing contradicted by service TTS text and any extra speech in the capture are INCONCLUSIVE; inconclusive attempts no longer count toward quarantine or the three-failure pause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… turner Dashboard pieces still pointed at producers that left the bridge in the #36 / #111 cutovers: - Activity feed opened /api/perception/feed on the bridge (404). The bridge now relays dotty-behaviour's SSE stream at /ui/perception/feed. - Emoji / Say / Story / Dance waited 8 s on a turn stream nothing publishes to, then reported "no reply". They now return as soon as the inject is accepted. - Voice tools card listed 5 of the 7 dotty-pi-ext tools; a test now pins the list to dotty-pi-ext/src/tools/. - Bridge detail said "Raspberry Pi"; robot "Last seen" ignored chat activity now that turns no longer land in the bridge's convo log. Sound localizer (#27, still stuck-left): - Remove the sound-balance sparkline from the State card along with its getter and dotty-behaviour's /api/perception/sound-balance endpoint. - Gate SoundTurner behind SOUND_TURN_ENABLED (default off) — every sound_event reports direction "left", so it snapped the head left on any noise. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first pi_voice persona fixed the coding-assistant leak but made the 4B chattier: offline, 'repeat these five words' was refused or hedged 3/8 and 'just say the answer' gained follow-up questions. With an explicit brevity rule: repeat 8/8 exact, arithmetic 4/4 bare answers, self-review 8/8 about itself with no coding-assistant mention. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dashboard's Turns tab, error toast, Errors count and error report all read conversation turns the bridge stopped seeing when voice moved to dotty-pi (#111): /ui/events had subscribers and no producer, and the convo log the Errors readers parsed was never written. The Content filter card had the same gap for kid-mode hits on the voice path. - PiVoiceLLM posts each completed turn to the bridge at POST /api/voice/turn and each kid-filter hit to POST /api/voice/filter-hit. Fire-and-forget on a daemon thread with a 2 s timeout, so a missing dashboard never delays or breaks a turn. Target is DOTTY_DASHBOARD_URL, else the dotty-behaviour host on :8081. - The bridge keeps the last 200 turns in memory only (nothing on disk), fans them out on /ui/events, and the Errors count / report / last-seen now read that ring instead of the dead convo log. - Both endpoints require X-Admin-Token when DOTTY_ADMIN_TOKEN is set. Closes #183 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pt is not extra speech Refs #182. The unattended trial retried one case 11 times because every attempt was INCONCLUSIVE (the reference mic always mishears one word of the reply), which neither completes nor fails a case. After max_inconclusive (3) such attempts the case moves to checkpoint['unresolved'] and the run moves on. Also: the first recognition in a capture is the prompt even when misheard. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code review (2026-10-21, slice A): the direct think_hard route yields the 27B reasoner's answer verbatim. The reasoner's only instruction is 'answer concisely', and since 122de9c the kid rules, English-only and length caps live only in the 4B's system prompt, so in Kid Mode only the regex output filter stood between the reasoner and the child. Before this PR the answer went back through the 4B. In Kid Mode the answer is now passed to the 4B as REASONING RESULT context and retold under the rules (the kid filter still runs on the result); outside Kid Mode it is spoken directly as before. dotty-pi-ext strips the new block from logged turns. Also corrects the comment on the inner output guard: it is kept for its buffering (a turn that fails part-way drops its partial reply), not as the only filter pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code review (2026-10-21, slice A): after 91f516c aborts a runaway turn it returns 5 s later even if pi never sends agent_end. think_hard ignores the abort signal and can wait 90 s on a cold 27B, so the aborted run's late text and agent_end landed in the shared queue and ended the next voice turn early ('(no response)', or 'agent_end before prompt-accept' -> 'brain offline'). On that timeout PiClient now stops the pi process; the next call respawns it with the same flags and system prompt. Each stdout reader is bound to the queue it started with, so a late frame from the old process cannot reach the new one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Code review (2026-10-21, slice A): startToChat calls _note_barge_in whenever Dotty is speaking, and the admin inject-text route (used by dotty-behaviour's face greeter and the dashboard) and the end-of-session prompt both enter through it. Injected text could therefore count toward #105's four-in-60-s storm and close a conversation, contradicting the comment that admin paths do not count. startToChat takes injected=True from those callers and skips the counter; the ASR path is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…f each message' Code review (2026-10-21, slice A): since 122de9c the voice rules follow the persona in pi's system prompt and the user's words arrive bare, so the persona's closing line pointed the 4B at rules that are no longer there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… on it Code review (2026-10-21, slice B): _load_state accepted the persisted file only if every device held one of the six durable states, so one probe that posted state 'testing' discarded the robot's saved sleep/security state too, and a non-UTF-8 file or a list-valued state raised past the (OSError, JSONDecodeError) handler and stopped dotty-behaviour starting. Drop invalid entries individually and start empty, with a warning, on any unreadable file. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e policy Code review (2026-10-21, slice B): vision_explain's get_kid_mode() and the ProactiveGreeter read app.state.kid_mode, which is set to False at startup and never changed, so every VLM description (room views included) and every proactive greeting used the adult prompt even in Kid Mode, the default. Add config.kid_mode_on(), which reads the shared guardian file on each call and fails closed (missing or unreadable means Kid Mode), and use it for both. Brett chose to keep room views in Kid Mode with the child-safe prompt rather than turn them off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dotty-behaviour hard-coded /mnt/user/appdata/dotty-bridge/state for the
read-only Kid Mode policy mount; with the bridge state elsewhere the file
is missing and the camera fails closed for ever. Use
${DOTTY_BRIDGE_STATE_DIR:-…} with the same default (DOTTY_STATE_DIR already
names this daemon's own state dir here).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… lookup Code review (2026-10-21, slice B): _perception_last_chat_ts read every device in the perception state, so a probe or the 'unknown' id could mark the robot 'active' while it was offline; it now goes through candidate_devices like every other device read. The status strip also made an uncached xiaozhi admin call on every render (every perception event, in every tab); cache it for _PROBE_TTL like the TCP probes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…olders Slice D/C review (2026-10-21): dashboard_smoke.py defaulted to the maintainer's dashboard IP, a test and a test skip message used the same IP, and a new dashboard test used the robot's real MAC. The smoke script now takes the URL as an argument or DOTTY_DASHBOARD_URL and refuses to guess; tests use TEST-NET (192.0.2.x) and a made-up MAC. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Slice D review (2026-10-21), fixed partly by a subagent and reviewed: - docs/dotty-overnight.md carried the maintainer's SSH login (root@tower) and the robot's MAC; now <XIAOZHI_USER>@<XIAOZHI_HOST> and <DEVICE_MAC>. - README: the default vision model is a cloud API (OpenRouter), not local; the output filter covers every LLM voice reply but not server-pushed speech (greeter, admin say); the rules live in pi's system prompt. - kid-mode.md, troubleshooting.md, llm-backends.md, protocols.md, brain.md, modes.md: the per-turn 'sandwich' is now pi's system prompt (respawn on a Kid Mode change, tool-call cap); OpenAICompat still appends the rules. Danger tier described as an invitation-to-copy backstop; rules 1, 2, 7 and 4-12 numbering match the code. - SETUP, quickstart, troubleshooting, reproducible-builds, COMPATIBILITY, firmware-release.yml: earlier fw-v* binaries were LAN-locked and removed; build from source; the release workflow is manual-only (#190). - uat-runbook UL3: the Kid Mode camera denial now exists. - dotty-overnight-clips.md: --crop trims source pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…view Slice C review (2026-10-21), fixed by a subagent and reviewed; each fix has a regression test that fails on the old code. - dotty-av-test.sh run mode no longer unmutes or sets the speaker; it re-checks the calibrated level right before playback and exits 3. - A FAIL verdict can never count as an acoustic pass (three late errors used to mark a case complete). - S05: the held sleep state is judged from a snapshot taken before cleanup wakes the robot, so it can pass. - A mic-transcript match no longer passes when the logged server TTS disagrees with the expected reply (INCONCLUSIVE). - Forbidden content spoken is FAIL even when the prompt was misheard or someone else spoke. - The reply-crop lag comes from a segment that holds prompt words, is capped at 1.5 s, and never crops before the prompt's last words. - An operator stop, the session deadline or a harness timeout is INCONCLUSIVE, not a quarantining failure (crash recovery unchanged). - wake_precondition is transient; the integration check retries a kid toggle restore and prints a loud banner if it never lands. - Evidence drops recognised identities (perception snapshots and face_recognized events). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An abort sets conn.client_abort, and upstream's TTS thread drops every queued message while it is set. Nothing cleared it until the next chat turn or a reconnect, so server-pushed speech after any abort was lost without an error. The face greeter, the proactive greeter and the A/V harness's mic opener all speak through this route; the 2026-10-22 run caught it when "Ready." after a song's cleanup abort never played. The say route now clears the flag before queueing, as play-asset already does. The aborted turn's leftover text is still dropped by the sentence_id swap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
The server's speech recognition misheard "Zorbo Quill" on every T03
attempt in the 2026-10-22 run ("Orbo film", "both quills", "orb of
quill"), so recall_person never had a name to look up. T04 and T03 now
use "Sam Baker" and blueberry pancakes; the pending-review checks are
unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
In the 2026-10-22 run the 4B solved the wugs puzzle correctly ("Not
necessarily…") without calling think_hard, and V07 failed for it. The
answer is what a user hears. T05 already shows think_hard works when
asked for, and pushing the routing rules toward the tool is what caused
this week's 19-call think_hard loop. V07 now passes on the right answer
and still fails a wrong one; any think_hard call is recorded in
observed_tool_calls.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
Asked "What do you know about my friend Sam Baker?" a second time in the same conversation, the 4B answered "I don't have any information" from its earlier lookup without calling recall_person (2026-10-23 A/V run, T03). Memory can change between replies (a pending fact approved on the dashboard, say), so the routing rules now ask for a fresh lookup on every question about a person or something remembered. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
… my friend X" In the 2026-10-23 A/V run the 4B answered "What do you know about my friend Sam Baker?" with "I don't have any information" and no recall_person call, even in a freshly spawned pi. 25dcb7c blamed reuse of an earlier lookup, but every voice turn already starts a new pi session, so that rule is removed again. Like the explicit remember / recall / think-hard phrasings (#165), a question about a named friend, neighbour, teacher or relative now runs recall_person itself. Nothing known gets a fixed "I don't know anything about <name> yet" without a model turn. recall_person never reads pending facts, so that is also what's said about a fact still waiting for review. Facts that are known go to the model as context, under the MEMORY SEARCH RESULTS marker the extension strips from the logged turn. "What do you know about dinosaurs" still goes to the model. The A/V harness's T03 accepts the direct-route log line as evidence, as T06 does for take_photo. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
…ecide (#192) "Please put on the song called tetris" got "I can't play Tetris because it's a puzzle game, not a song" with no play_song call (2026-10-23 A/V run, T07; the same failure on 2026-10-20). "Play/put on the song (or tune) called|named <x>" now runs play_song directly. The tool's catalogue match decides, not the model: - "playing tetris" becomes "Here's tetris!" and the song starts after the reply; - no match becomes "I don't have a song called <x>."; - anything else becomes "I couldn't play that song right now." "Can you play a song?" with no name still goes to the model. The A/V harness's T07 accepts the direct-route log line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
"What is 12 plus 10 room?" got "12 plus 10 is 22" in two of three tries in the 2026-10-23 A/V run (K02; #178 saw "22 room"). Rule 10's "a question gets its answer" won over rule 11's "ask again". Rule 10 now says a *clear* question gets its answer, and rule 11 says a word that doesn't belong means part of it was misheard: ask again instead of half-answering. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
This reverts commit 53d8cd5. On the robot it made no difference: "What is 12 plus 10 room?" still got "12 plus 10 is 22" in 2 of 2 tries. V03 was unaffected. The rules stay as they were, and K02's pass criterion goes to the maintainer instead of more prompt pressure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
S06 (dance) is speech-optional, but the runner honoured that only when a spoken reply couldn't be transcribed. In the 2026-10-23 run the firmware danced (talk -> dance -> idle) without any spoken reply, and the attempt failed as no_tts. no_tts now gets the same treatment: it passes only with the expected state observed and audio above the case's level floor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
Dotty greets the wake word straight away ("Hi ESP. How can I help you
today?", enable_greeting). W01 spoke "Hi E S P. What is your name?" in
one breath, so the question landed on top of the greeting and recognition
heard nothing. It has never passed.
A case can now set prompt_pause_after / prompt_pause_seconds. The renderer
voices both parts and joins them with silence, and the pause is recorded in
the renderer label, so changing it re-renders. W01 pauses 5 s after the wake
word.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
Greetings ran 2.4-4.2 s, but one reached 6.3 s and overran the 5 s pause (2026-10-23 A/V run). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
W01 played its wake word 4 s after Dotty's conversation timed out, while the robot was still reconnecting its WebSocket, and the wake word was missed (2026-10-23 A/V run). The attempt after it, on a robot already cold, woke at once. When await_cold has had to wait, it now gives the robot 15 s and checks it's still cold before returning. A robot that is already cold isn't kept waiting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
The model writes the greeting fresh each time; one ran to 7.3 s, up against the 8 s pause (2026-10-23 A/V run). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
…can wake it docs/modes.md says the dashboard (admin set-state idle) wakes a sleeping Dotty, but in the 2026-10-23 A/V run (S05) it couldn't: - cdce1df made an admin sleep close the WebSocket; - a sleeping robot never reconnects, because voice, wake word and perception are all gated off; - so set-state idle had no device to reach; - even without that close, xiaozhi's inactivity check (no-voice time + 60 s) drops a device that sends no audio. Changes: - admin sleep no longer ends the session (idle still does); - on a state_changed=sleep event, keep_alive_while_asleep refreshes the connection's last_activity_time every 30 s until the state changes or the connection stops. The firmware privacy gate keeps the mic and wake word off while asleep, so holding the socket open hears nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
…keeps its voice
After a restart in the 2026-10-23 A/V run, the robot connected twice in
the same second:
- both connections built their TTS on worker threads;
- upstream's create_instance treats any sys.modules entry as loaded, so
one connection got the half-imported piper_local ("partially
initialized module ... has no attribute 'TTSProvider'");
- the connection that stayed had no TTS, so /admin/say returned 503 "device
has no tts provider" and Dotty could not speak until it reconnected.
WebSocketServer now imports the configured TTS module once on the main
thread before accepting connections. This is best effort: any problem is
left for the per-connection path to report as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
S05 checks that a sleeping Dotty says nothing. With the reply hotword "Dotty", Whisper heard a lone "Dotty" in the silent crop twice in the 2026-10-23 run. The server had logged no speech and no reply, but the attempts came out inconclusive (speech_in_silence_window). Silence cases now transcribe without the hotword. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
Moves the CHANGELOG's Unreleased entries under [server-v0.2.0-rc.1] - 2026-10-24 and bumps the code-level versions (dotty-pi-ext 0.2.0-rc.1, dotty-behaviour VERSION 0.2.0-rc.1). Docker image tags stay at 0.1.0: they're shared with the deploy scripts and the host compose files. The tag itself is the maintainer's to cut. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NRqp6NDnGKg1DWFd9qrkjW
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Collects everything intended for
server-v0.2.0-rc.1on one branch so the Docker host runs a single commit.Contents
test/overnight-av-20260912: voice-state, ASR-intent and camera-policy fixes plus the A/V test harness.--no-ff):dangertier. OpenAICompat follows the live kid toggle.play_songreaches xiaozhi (XIAOZHI_HOST), MIDI songs play, a mid-reply song starts after the reply (after_turn). Memory: voice turns are logged with prompt scaffolding, and recall speaks stored rows verbatim #186 and think_hard fails on a cold start: 30 s timeout is shorter than the reasoner's load time #187 were already on the branch.admin/end-session; Voice wake exit is lost after WebSocket reconnect in sticky states #170 sticky state restored on reconnect; say bypass races chat turns under continuous ASR — sentence_id arbitration #104 server-push audio kept (image rebuild); UAT US8/US7: ASR mishears the wake-name "Dotty", breaking name + state-trigger phrases #164 Whisper hotwords, name corrections only when addressed; Ambient ASR garbage — background audio (TV/podcast) feeds Whisper as fake user input #105 four barge-ins in 60 s end the session.vision/latestno longer deletes the photo; closing a tab no longer freezes the bridge.LD_LIBRARY_PATH(every utterance failed after an image rebuild) and pinned pip versions; a lone hotword "Dotty" needs a stricter no-speech score (Ambient ASR garbage — background audio (TV/podcast) feeds Whisper as fake user input #105, UAT US8/US7: ASR mishears the wake-name "Dotty", breaking name + state-trigger phrases #164);memory_lookupfalls back to keyword overlap when one word is misheard; PiClient's INFO log lines reach the xiaozhi log; "use your camera" routes totake_photoinstead of an invented room; a post-tool turn that only repeats the pre-tool line is dropped.122de9c); PiClient caps tool calls per reply, steering after 3 and aborting after 6, andthink_hardis for hard questions only (91f516c); the Kid Mode jailbreak rule no longer refuses "repeat these words" (a84ee5b,a9d6040); ASR pre-roll 1.2 s (45a0449, image build patch) and tower VAD silence 800 ms (config)./admin/sayspeaks after an abort (cf5243d).recall_person/play_songdirectly (68a834d,be2d16a; Voice: pi sometimes says a catalogue song is missing without calling play_song #192).f7630b3).d80b5e1).[server-v0.2.0-rc.1] - 2026-10-24, with code versions bumped (5651a59).scripts/dotty_integration_check.py,scripts/dashboard_smoke.py,scripts/dotty_render_prompts.py, A/V cases for all seven tools and the states.Deferred
ttl(deployment config, not repo code).contextWindow4096: fits (~3K per kid-mode turn) but thin; measureprompt_tokenslive.min_silence_duration_ms600); tuning trades reply latency.fw-*tag), onstackchan-fwagent/fw-release-fixes.Verification
pytest tests/ custom-providers/pi_voice/tests/: 802 passed (at5651a59).pytest dotty-behaviour/tests/: 316 passed.ruff check .: clean./code-review ultra; the test harness and docs went to subagents. Every finding was checked against the branch, and every real one was fixed with a test (9ab676btoe960351).d80b5e1(xiaozhi; dashboard and dotty-behaviour ate960351). The robot reconnected with 15 tools, with no component errors.memory_lookup, T02remember, T03recall_person, T04remember_person, T05think_hard, T06take_photo(the Kid Mode refusal), T07play_song.StoreProhibitedpanics intlsf_malloc, each about 1 s after the server's idle close and reconnect. The likely cause is thatEspTcp's destructor doesn't wait for its receive task after a server-side close. A fix is drafted onBrettKinny/StackChan@agent/fw-tcp-teardown(94def10), not yet built.ESP_ERROR_CHECKinCoreS3AudioCodec::EnableOutput), possibly the same supply dip as the power losses.take_photohang (Firmware main loop hangs when take_photo arrives before the camera stream has started #189).Not for unattended merge
Per
AI_TRANSPARENCY.md, a human reviews and merges. Squash-merge this PR: its history includes a WIP snapshot (258b9e2) whose stray files were removed in061fe12, and squashing keeps them out ofmain. Deleterelease/server-v0.2.0-rc.1after merging: its history also has the maintainer's host and robot MAC in4e5aedb(fixed at the tip in67c91ff). This PR was assembled by AI agents (Claude Opus 5.5) under the maintainer's direction.🤖 Generated with Claude Code