arizuko › concepts › engagement
engagement
Routing delivered the message to a folder. Now engagement decides whether to keep listening. Mentioning the bot for every single message kills conversation flow, so once the bot has replied in a (chat, topic), that pair is “engaged” for a window of time. Subsequent inbounds in the same pair fire the agent without needing another mention.
two states
A (jid, topic) pair is either engaged or idle.
- Idle → engaged: a mention, a reply to a bot message, or an explicit
engage()call. The bot replies, and the timer starts. - Engaged → engaged: every inbound while
now − last_activity < ENGAGEMENT_TTLfires a turn, no re-mention needed. - Engaged → idle: the TTL expires, or the agent calls
disengage()when it’s done helping. - Idle stays idle: after disengage, the next inbound needs a fresh mention.
The TTL is sliding — reset by any outbound reply from the bot while engaged. So a long back-and-forth never times out mid-conversation; a quiet 20 minutes does. The window is configured via ENGAGEMENT_TTL (default: 20m).
thread by default on Slack channels
The bot’s reply to a channel mention opens a Slack thread off the user’s message. The engagement window then attaches to that thread. Subsequent thread replies engage automatically and live in-thread, so the main channel stays uncluttered.
Broadcasts (scheduled tasks, migrate announcements, system events) bypass the rule — they have no parent message to thread under, so they land top-level.
corrective side-fork
When the user is correcting the bot’s last reply (“no, that’s wrong”, “rephrase”) rather than continuing the conversation, the agent can fork the topic to <current>#fix and run the correction loop there. The main thread stays clean; the fix loop happens in a child thread off the bot’s incorrect reply. Convergence reached, the agent posts one corrected answer to the parent and calls disengage() on the fix topic.
This is a convention — the agent decides when to fork. routd doesn’t enforce it.
length per surface
Every turn carries a <surface> hint so the agent can self-cap reply length:
<surface>slack-channel-thread</surface> threaded reply, full ceiling
<surface>slack-channel</surface> rare: top-level, hard-cap 200 chars
<surface>slack-dm</surface> full ceiling
<surface>slack-pane</surface> assistant sidebar, full ceiling
<surface>discord-channel</surface> default ceiling
<surface>telegram-dm</surface> full ceiling
The hint is computed from the chat JID shape and thread context. The agent reads it and obeys.
MCP control
engage(jid, topic)— explicit re-engagement, e.g. before a scheduled autonomous turn.disengage(jid, topic)— clear the engagement so the next inbound needs a fresh mention.
Both args are required — the agent passes the jid it intends to act on.
(chat, topic) is engaged, routd delivers to the engaged folder and fires a turn regardless of what the route table says — including #observe targets, trigger routes pointing elsewhere, or no matching route at all. The bot is mid-conversation; the route table is irrelevant until the TTL expires or disengage() is called.go deeper
The mention check, observe-wins ordering, and TTL math live in specs/5/G. The fork primitive used by the corrective pattern: topics. Routing modes that interact with engagement: routing modes.