topics
Engagement keeps turns flowing to the same folder; topics keep two conversations inside that folder apart. A topic is a label on a message that splits one folder’s chat into separate threads. Each thread has its own session and its own reply chain. Messages in #support don’t see messages in #billing.
where topics come from
routd reads the topic from one of three places:
- Platform-native threads. Telegram’s
MessageThreadID, Discord’s thread channel ID, and similar fields land onMessage.Topicwhen the adapter hands the message toroutd. - The web channel. A web message carries a topic slug directly — the chat widget chooses it before the request goes out.
- The
#topicprefix. When a user starts a message with#name,routdstrips the prefix and runs the rest under topic#name.
what a topic isolates
The agent keeps one Claude session per (folder, topic) pair. So /new #support resets the support thread without touching anything else in the folder. The store also tracks the last bot message per topic, so reply threading on the platform stays clean — replies anchor to the right message.
For the web channel, routd groups inbound messages by topic and runs one agent per topic, one after another.
forks
A fork copies the parent topic’s Claude Code session file to a fresh session UUID. The child resumes natively from the parent’s tail — every prior turn is already in the child’s history, because it got the real session file, not a pasted-in summary. The sessions row records parent_topic and forked_at for audit.
Three triggers all map to one ForkTopic(folder, parent, child) call:
- Default fork from main — the first turn in a brand-new topic forks the folder’s main session.
- Reply-to-parent — when the trigger message is a reply, the parent is the topic of the replied-to message, not main. Reply inside
#deploystays in#deploy; reply to a#triagemessage from a fresh thread forks from#triage. - Explicit
fork_topicMCP — the agent branches a focused side-conversation. Passforce=trueto overwrite an existing child.
On every turn the agent receives a <topic name="X" /> envelope so it knows what scope it’s in. There is no <inherited> block — the parent’s history is already in the child session.
sessions.observed_cursor is per topic too, so two topics in the same folder both see the ambient <observed> sidechannel without one consuming it for the other.
sticky topic
A user can pin a chat to a topic by sending #support on its own as the entire message. routd stores it on the chats row (sticky_topic), and every later message in that chat runs under #support until someone sends bare # to clear it. A sticky topic wins over the thread ID the platform sent.
go deeper
Topics ride inside one folder; routing picks the folder first, then topic isolation kicks in. How folders relate to each other and what counts as ambient context: scopes. Full fork mechanics and the per-topic observed cursor: specs/5/F. Resolution order: ROUTING.md.