Repository navigation
fix(perch, pi-bots): a bot that needs you waits for you, and pings you once - #534
Merged
Merged
Conversation
…RKSPACE / PI_BOT_PROJECTS_ROOT / PI_BOT_ID), fail-closed Every bot child whose bot has a project workspace gets three engine-reserved keys so pi-lab's bash check can stop treating the bot's own workspace as a sensitive ~/.crow path. Set only for a real <data>/projects/<segment>/workspace directory (realpath'd, so .. and symlinks can't escape); a def's spawn_env can't set them (PI_BOT_ prefix is stripped). Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
…ngs you once - Ask cards are grouped per session: one high-priority "needs you" row and push while it is unread, updated in place with a count and every question; one trailing reminder per 10 min only while a card still waits; answering, cancelling, aborting or stopping closes the group. - Perch children get PI_BOT_INTERACTIVE_ASK_TIMEOUT_MS=2147483647 so silence never becomes a "no". A request's own timeout (older pi-lab) or a crow-state ask-parked signal (newer pi-lab) expires the card: it stays visible read-only, the session still shows as waiting, a late answer gets 409 request_expired and runs nothing, and the parked turn ends silently. An expired card no longer pauses the stall watchdog or blocks hibernation. - Card execute refuses (409 waiting_on_operator) a card whose Perch session is waiting on the operator. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
- Workspace keys go to interactive (Perch) children only; channel and job children never get them, and an inherited copy is dropped first. - A question that dies with its child, or that an older pi-lab already answered "no" before the turn finished, closes its group and stops holding the board card; a park signal only silences a turn when it expired a real card. - A reminder held back by a snooze is retried when the snooze ends. - One settleAsk/dropAsk helper per outcome; the combined-card drift path keeps its expiry and group. Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
…ses its needs-you group Claude-Session: https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp
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
A bot in one Perch session sent 44 high-priority "needs you" pushes (62 across a weekend), the closest 8 s apart. Two things combined:
A third cause: the bash safety check treats any path under
~/.crowas sensitive. That includes the bot's own project workspace, so routine commands there asked for approval.This PR is the crow half. The pi-lab half (no "no" on a timeout, parking instead, and the workspace exemption itself) ships separately, against a written contract. Either half can ship first.
What changes
One push per question episode (
servers/shared/notifications.js,servers/gateway/perch-interactive.js)metadata.group(the newest 50 are kept, and older ones are counted).created_atis left alone, so the Android app'screated_at > sincepoll does not alert again on an update.PERCH_ATTENTION_REPUSH_MS(default 10 min). It goes out only while a card is still waiting and the row isn't snoozed. The last push time and count are stored on the row, so a gateway restart doesn't reset them.createNotificationbehaves as before. Its push fan-out moved into an exporteddeliverNotification.A waiting question pauses the session instead of turning into a "no" (
perch-interactive.js)PI_BOT_INTERACTIVE_ASK_TIMEOUT_MS=2147483647, the largest Node timer value. A pi-lab that still denies on timeout now waits about 24.8 days instead of 2 minutes.timeout, and crow expires the card 2 s before it;crow-state:{"kind":"ask-parked","reason":"timeout"|"aborted"}.409 request_expired. Nothing is sent to pi and nothing runs.abort()clears it, and the next message clears it.PIBOT_MAX_PIslot, so background jobs may queue behind it. This is documented indocs/developers/bot-engine.md.The board doesn't start a second turn on a waiting card (
servers/gateway/routes/bot-board-api.js)/card/:id/executereturns409 waiting_on_operatorwhile a Perch session attached to the card has a live question, or an expired one on an awake child.The project's workspace, for pi-lab's bash check (
scripts/pi-bots/bot-workspace-env.mjs,bridge.mjs,bot-world.mjs,job_runner.mjs,instance-paths.mjs)PI_BOT_WORKSPACE,PI_BOT_PROJECTS_ROOTandPI_BOT_ID. Channel and job children never do: a channel model reads untrusted mail, and a job runs bash as deny.<data>/projects/<one segment>/workspaceand the bot id is a plain id. Otherwise none is set, and pi-lab then grants no exemption.CROW_BOT_ENV_ALLOW) is deleted first. The keys carry thePI_BOT_prefix, so a bot def'sspawn_envcan't set them, andspawn_envcan't make a child interactive either.<ws>/bots/<id>/). Answer:<ws>/bots/<id>/is each bot's engine world root. It holdssessions/(pi transcripts, resumed on every wake),outputs/, uploads,.pi/and.mcp.json. The pi-lab side keeps<ws>/bots/**entirely outside the exemption, the bot's own folder included, along with every dot-segment, and an older pi-lab ignores these keys. Since a Perch bot's default cwd is that same folder, routine relative paths never meet the.crowrule anyway; only absolute paths into it stay flagged, as today. The exemption skips one tripwire only: the classifier, write confinement and every other rule still apply.Tests
tests/notifications-grouped.test.js,tests/perch-interactive-needs-you.test.js(the flood replay, grouping, serialised bursts, the re-push window, expiry, late answers, parking with and without the signal, ask_user abort, a park during a combined-question replay, reconnect replay,waitingOnCard),tests/bot-workspace-env.test.js(..traversal, symlink escape, the root itself, wrong shape, bad ids).tests/pibot-read-confinement.test.js(workspace keys reach channel and job children; a def'sspawn_envcan't set them) andtests/board-dispatch-job-rail.test.js(execute refuses a waiting card).extraEnvassertions and the fd-leak test intests/perch-interactive.test.js, to match the grouped semantics.npm test): 10418 tests, 10381 pass, 35 skipped, 2 fail. Both failures are in files this PR doesn't touch (tests/artifacts-panel.test.js"live QW4 B2/B3" andtests/workspace-calendar.test.js"T10-I3"), and both pass when run alone. They look like load-timing flakes. The earlier run had only the artifacts one.Reviews
created_atisn't bumped (the Android poll would re-alert), answering closes the group, snooze is respected, expired cards are replayed and abortable, and the workspace check is tightened to the exact shape.settleAsk/dropAskhelpers.PIBOT_MAX_PIslot until it's answered, cancelled or aborted (operator decision; a crow-side park cap is the follow-up if it bites). A pre-existing, out-of-scope issue: the combined-question replay (danceStep) should only answer ask-user dialogs.Deploy
crow-gatewayandcrow-r4-gateway(engine, routes, notifications, clients).scripts/pi-bots/changed, sopibot-gateways@r4needs a restart too. Note the timestamp.https://claude.ai/code/session_01N9fXzvNy8rCEArj1y5p6fp