Repository navigation
feat(voice): reserve the Crow voice bot, so it can't be deleted, edited through generic saves, peer-managed, delegated to, scheduled or given cards - #528
Merged
Conversation
…gated to or given cards The reserved crow.voice row is refused by Bot Builder delete (voice_bot_permanent) and every generic Bot Builder action (voice_bot_readonly), is never peer-manageable, is left out of the peer catalog, the Bot Board list, the crow-messages roster and the Perch /bots and /roost listings, cannot take crow_delegate or crow_schedule_bot jobs, cannot be given cards (400 bot_unavailable), and is skipped by the artifacts-companions migration. Bot Builder's create rule is exported as sanitizeBotId. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
…e Crow voice bot; Bot Builder delete shows voice_bot_permanent POST /card/:id (assign), /card/:id/execute and /bot/:botId/proposed-skill/approve refuse crow.voice with 400 bot_unavailable before any card, job or definition write. handleBotBuilderPost lets delete_confirm through to handleDeleteConfirm's own refusal; VOICE_BOT_ACTIONS stays the voice editor allow-list. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
… the Crow voice bot tickBotSchedules advances a pipeline:botcron:crow.voice schedule like any it cannot enqueue (no bot_jobs row, the schedule is kept). runJob fails any job whose bot id is crow.voice or whose definition is kind voice, before any bot load or pi spawn, so every producer is covered. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
kh0pper
force-pushed
the
feat/crow-voice-pr0-guards
branch
from
October 20, 2026 19:49
8536795 to
cf89b4b
Compare
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.
Why
Crow's voice will become one reserved bot,
crow.voice, stored inpi_bot_defswithdefinition.kind = "voice". A later PR creates that row at boot. This PR ships first and adds no row of its own. It adds the definition module and every guard the row needs, so the row is never unguarded: not when it first appears, and not if the later PR is reverted.What changes
servers/shared/voice-bot.js(new, no I/O)CROW_VOICE_BOT_ID = "crow.voice",isVoiceBotId,isVoiceBotDef.fast_local(the local voice model) andcloud(needs a cloud model; turns escalation off).DEFAULT_PERSONA.voiceBotDefinition({ preset, fastKey, cloudKey, migratedFrom })builds a definition that can never do pi work: no gateways, no tracker, no skills, no pi built-ins, a short tool list (memory, media, music), and a permission policy of bash deny, drafts only, no self-authoring and no skill learning.normalizeVoiceSettings(def)reads the voice settings and falls back to the strict default for anything missing or malformed. It never falls back to something looser.Guards, one check each
crow.voiceis refused withvoice_bot_readonly. Delete reacheshandleDeleteConfirm, which answersvoice_bot_permanent.sanitizeBotIdis exported, and create uses it with the same rule as before.botPeerManageableis false for the voice bot, and the peer catalog (localBots) leaves it out./botsand/roostleave it out.crow_delegateand the bot-schedule tool refuse it with "cannot take background jobs".POST /bots/:id/tasks) gives 400bot_unavailable.bot_unavailable.No schema change, no
SCHEMA_GENERATIONbump, no DELETE or DROP. No bundle code changes, so there are no manifest bumps.Tests
tests/voice-bot-def.test.js: presets, normalization of malformed input, the reserved id, andsanitizeBotId("crow.voice") === "crow-voice".tests/voice-bot-guards.test.js: a scratch DB frominit-dbwith acrow.voicerow and two ordinary bots, using the real handlers and routers.bot_jobsorschedulesrow is created, and the card is unchanged.Deploy
Merge, then pull
mainon each host and restart the gateway(s) and the pi-bot gateway runner. Nothing changes for ordinary bots, and nothing is visible until the voice bot row exists.Rollback
Revert this PR only after any later voice PR has been reverted and the voice row has been disabled (
UPDATE pi_bot_defs SET enabled = 0 WHERE bot_id = 'crow.voice', a disable, never a delete).https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp