Skip to content

telegram: spawn-approval delivery skips the channels governance ceiling, so a denied channel denies the spawn by timeout #13491

Description

@chenmingwei23

What happens

TelegramDispatcher.deliver_spawn_approval posts the spawn-approval prompt without consulting the channels governance ceiling. An operator profile that denies the telegram channel after the transport connected therefore does not stop a spawn-approval prompt, carrying an agent-authored task preview, from being posted into that conversation.

Why it matters

The prompt cannot be answered once it is posted. telegram/transport_dispatch.py drops an inbound callback press on a governance-denied channel, and only an explicit reject (a:<rid>:<nonce>:0) is exempt from that drop. So an Approve press on a denied channel resolves nothing, the deny-by-default wait runs to APPROVAL_TIMEOUT_S, and deliver_spawn_approval returns False.

That False is not a fall-through. The delivery seam reads it as the user's real decision, so the host gate refuses the spawn on it instead of re-offering the prompt on the still-permitted Slack DM or dashboard surface. The user sees a spawn denied by a prompt they were never able to see, after a full timeout.

Where

src/kiro_crew/telegram/transport_dispatch.py, deliver_spawn_approval. The method consults the local rosters and _spawn_prompt_destination_permitted, neither of which speaks for the governance profile. channel_inbound_permitted is already imported in that module and used on the inbound path and the callback path, but not on this delivery path.

The startup gate does not cover this: it only stops a transport from connecting. A profile that denies the channel afterwards leaves the transport up with its rosters intact, which is the gap _channel_inbound_permitted_sync exists to close on the other paths.

Suggested fix

Consult channel_inbound_permitted("telegram") at the top of deliver_spawn_approval, before the nonce is armed, and return None on a deny so the gate falls through to Slack/dashboard. The check has to sit before the arm rather than beside the roster check: the roster check is deliberately synchronous with no suspension point before the send it guards, and an await between them would reopen that window.

Notes

Found while addressing a review finding of exactly this shape against the Discord port of the same seam in #13454, where the check was added. Kept out of that PR so it does not widen a converging review round. The two dispatchers share the seam but not the code, so fixing one does not fix the other.

Activity

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

    area: agentsACP runtime, sub-agents, session lifecyclearea: channelsSlack, Discord, WeCom, notification routingbugSomething is not working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions