feat: deliver spawn approvals to the originating telegram channel - #9242
feat: deliver spawn approvals to the originating telegram channel#9242cschnidr wants to merge 3 commits into
Conversation
…#2381) Address the four non-blocking review issues on the approved v1: - Pin the armed-key vs recomputed-key coupling: add a test that rotates the parent conversation's generation between spawn and press and asserts the prompt deny-by-defaults; note the invalidation in the deliver_spawn_approval docstring. - Cover the CHAT_TYPE_FORUM branch of _spawn_chat_target with a forum-parent test asserting the prompt is threaded into the originating Topic. - Confirm a cancelled awaiting decider mid-prompt leaks no registry/nonce entry (the __call__ finally already covers cancellation). - Align the seam-level delivery-failure warning to also name the request id, matching the dispatcher-level warning's granularity.
|
👋 Hi! This PR's description is missing some required sections from our PR template. Workflow runs won't be auto-approved until the description is updated. Missing sections:
Please update your PR description to include these sections, then push or re-save the description. The workflows will be approved on the next cycle. |
Design Review (Fable 5.1, fork) — ✅ PASSDesign-level review of Design-Verdict: PASS Sound, proportionate fix: a channel-neutral seam gives Telegram-parented spawns the originating channel's own approval keyboard, mirroring the established Suggestions
[DESIGN-REVIEWED] 8954ee8 |
First Principles Review (Fable 5.1, fork) — ✅ PASSPremise-level review of Verified: First-Principles-Verdict: PASS Delivers spawn approvals to the Telegram chat that started the turn via a seam every channel can register into; each item removes a named harm and reuses existing infra. What this change shipsIntent: let a Telegram user answer a sub-agent spawn approval in the originating chat instead of watching the dashboard or eating the #8914 fast-fail. ADDITION (completes issue #2381 item 1).
Generality is bounded by counted siblings, the seam falls open to the preserved #8914 backstop, and every rider is declared. No item's zero option is free; none duplicates an existing mechanism; the fix sits at mechanism/cause level. [FIRST-PRINCIPLES-REVIEWED] 8954ee8 |
GPT 5.6 Review (fork) — ✅ no blocking findingsReviewed Review detailsFINDING -- src/kiro_crew/telegram/transport_dispatch.py:2733 -- FINDING -- src/kiro_crew/telegram/transport_dispatch.py:2769 -- ignoring [GPT-REVIEWED] 8954ee8 |
Opus 4.8 Review (fork) — ✅ no blocking findingsReviewed |
|
Heads-up for a maintainer: this is a fork PR and the latest workflow run is sitting at action_required, waiting for an "Approve and run" click. Context: the only red gate on the previous run was PR Hygiene → "Validate PR title (conventional commits)". The title lacked a type prefix. I've fixed it to Once someone approves the pending workflow run, PR Hygiene should re-run with the corrected title and PR Readiness should flip green. Thanks! |
Problem / Motivation
Follow-up to merged PR #8914 on issue #2381. #8914 fixed the 1800s hang: a subagent spawn whose approval prompt reaches no surface now fast-fails instead of stalling until the reaper. But issue #2381 item 1 stayed open: a spawn approval still could not be answered from its originating Telegram channel. The single host-wide spawn gate raced only a Slack owner DM and the dashboard, so a spawn parented on a Telegram conversation had no in-channel way to be approved. A Telegram user either watched the dashboard or the spawn fast-failed.
Why it matters
Subagent orchestration is effectively unusable from Telegram: every
spawn_runfrom a Telegram-originated turn cannot be approved where the user actually is. Post-#8914 it fails fast rather than hanging, which is better than a 30-minute silent stall, but the user still cannot say "yes, run it" from the channel that started the turn. Delivering the prompt in-channel (with an optional Trust action) restores spawn orchestration for chat-channel users and realizes the issue's suggested fixes (a) deliver the prompt to the originating channel and (b) let a trusted session auto-approve future spawns.What changed (motivation → approach → change)
Goal: let a Telegram user answer a spawn approval in the originating channel, without weakening the #8914 fast-fail backstop and without adding the per-agent auto-approve rung (issue item 2, deferred by the commenter to #4751/#4693).
Approach: rather than teaching the host spawn gate about every channel (the coupling the gate exists to avoid), introduce a channel-neutral delivery seam modelled on
messaging/session_trust.py. The host callback consults the seam first; a channel that owns the parent session registers a hook and answers in-channel; anything else falls through to the unchanged Slack-DM/dashboard path, and then to the #8914 fast-fail backstop. Fall-through is fail-open toward the existing gate: no hook, an unowned/non-channel key, a hook returningNone, or a hook that raises all collapse to the same "fall through", so a channel-delivery bug can never turn an answerable spawn into a hard failure and can never bypass the backstop.What was built:
src/kiro_crew/messaging/spawn_approval_delivery.py: an in-memory, process-global registry mapping a channel namespace (telegram,slack, …) to an async delivery hook(request_id, description, parent_session_key) -> bool | None. Register/unregister/resolve/deliver, keyed bychannel_namespace_of(parent_session_key), raise-contained toNone.src/kiro_crew/slack/gateway.py_spawn_approve) now consults the seam first;True/Falseis the in-channel decision,Nonefalls through to the unchanged_approve_spawn_gate(Slack DM + dashboard, still raisingSpawnApprovalUnreachablewhen nothing is attached — the fix(subagent): refuse a spawn whose approval prompt has no surface (#2381) #8914 backstop, unchanged).src/kiro_crew/telegram/transport_dispatch.pydeliver_spawn_approval+_spawn_chat_target): reconstructs the originating chat from the parent session key grammar, posts an Approve/Deny/Trust inline keyboard forspawn:<id>, and awaits the press via the existingTelegramApprovalDecider/on_callbacka:path. Trust routes throughadd_trusted_sessionso future spawns from that session auto-approve, scoped to exactly the parent session key the prompt was armed under.src/kiro_crew/telegram/gateway.py,src/kiro_crew/telegram/client.py,src/kiro_crew/telegram/renderer.py): the hook is registered on gateway startup and unregistered on client close (newon_close);TelegramApprovalDecider.retire()drops an armed nonce whose prompt never posted.docs/system-specs/modules/subagent.mdanddocs/system-specs/modules/messaging.mddocument the delivery order (channel hook → Slack-DM/dashboard fallback → fix(subagent): refuse a spawn whose approval prompt has no surface (#2381) #8914 fast-fail backstop) and the seam.Tests
test/test_spawn_approval_channel_delivery_2381.py(new): the seam's register/resolve/unregister/isolation/replace, raise-containment toNone, and theNonefall-through; the wrapped host callback's True/False/None/no-hook behavior; a source ratchet that_spawn_approveconsults the seam before the gate; the Telegram hook's Approve→True, Deny→False, Trust→True-plus-add_trusted_session(withis_session_trustedasserted), the three-button keyboard shape, non-telegram fall-through, failed-post retiring the nonce, generation-rotation expiry, forum-Topic threading, and cancellation-leak; and a precedence test that a trusted/auto parent never reaches the channel prompt.test/test_spawn_approval_no_surface_2381.py(the fix(subagent): refuse a spawn whose approval prompt has no surface (#2381) #8914 regression tests): unchanged and still green, confirming the fast-fail backstop and the separate tool-vs-spawn callbacks are intact.test/test_messaging_driver.py: unchanged, still green.Manual verification
N/A — unit coverage is sufficient. The delivery seam, the wrapped host callback, the real
TelegramApprovalDecider.deliver_spawn_approval, and theon_callbackpress path (including Trust →add_trusted_session) are all driven directly by the new tests rather than mocked away; the commenter on #2381 also offered to test the follow-up against their headless Telegram setup.Related Issues
Refs #2381
Follows #8914 (fast-fail backstop, preserved unchanged)
Defers the per-agent auto-approve rung (issue item 2) to #4751 / #4693