topics
The topic of a message, the four ways a topic starts, the order that picks the active topic, and the MCP tools for topic sessions. Topics explains the concept.
Three records carry topic state
| record | field | meaning |
|---|---|---|
| message | topic | The topic of the message. An empty string is the default topic. |
| chat | sticky topic | The topic pin of a chat. Empty means no pin. store/chats.go:272 |
| session | (folder, topic) | One Claude session for each folder and topic pair. |
The chat record also holds the sticky group, a folder pin. It is a different pin and appears here only to tell the two apart. schema lists the full store.
Four sources set a topic
- An inline
#nameprefix - A message whose text starts with
#nameopens the topic#name. The regex is^\s*#(\w[\w-]*),rePrefixHashinroutd/steer.go:443.handlePrefixLayerremoves the prefix, writes the rest as a new message withTopic = "#" + nameand queues the chat. - A bare
#namemessage - A message that is only
#namesets the sticky topic of the chat. A message that is only#clears it.handleStickyCommandinroutd/steer.go:49repliestopic → <name>ortopic reset to default. /newand/new #name/newalone clears the session of the default topic./new #name [rest]clears the session of#name, then writes#name restas a new inbound message, which goes through the prefix path.cmdNewinroutd/steer.go:411.- Platform threads
- Discord: a message in a thread gets the thread ID as its topic, under the JID of the parent channel, in
chatAndTopic,discd/bot.go:304. Telegram: a message in a forum topic gets theMessageThreadIDas its topic,teled/bot.go:396. Slack: a thread uses itsthread_ts. Other adapters leave the topic empty.
A sticky topic wins over the topic of the message
When routd forms a turn, effectiveTopic in routd/loop.go:681 picks the topic:
func (l *Loop) effectiveTopic(chatJID, msgTopic string) string {
if _, sticky := l.db.StickyState(chatJID); sticky != "" {
return sticky
}
return msgTopic
}
- The sticky topic of the chat, if it is not empty.
- The topic of the message, from the prefix or from a platform thread.
- The empty string, the default topic.
The result selects the Claude session of the turn.
#current, a message with #other still runs under #current. Send a bare # to clear the pin first.Two MCP tools act on topic sessions
reset_session(groupFolder)- Clears the Claude session of a folder, so the next message starts with a new context. The caller must hold
mcp:reset_sessionwith a scope that coversgroupFolder. Registered inipc/ipc.go:1738. inspect_session(topic?, limit?)- Returns the current
session_idof the own folder and the topic, with recentsession_logrows.limitdefaults to 10; a value of 0 or less, or over 100, gives the default. Registered inipc/inspect.go.
inject_message writes an inbound message, and the next turn applies the sticky and prefix rules to it as to any other message. No set_topic or get_topic tool exists. A sticky change goes through the #name chat command.
Observed messages ignore the topic
Every turn also gets the observed messages of its folder. store.ObservedSince in store/message_folders.go:284 reads them by folder and cursor, not by topic, up to OBSERVE_WINDOW_MESSAGES messages and OBSERVE_WINDOW_CHARS characters. routd calls it in routd/prompt_db.go:82. So a turn in topic #tickets-42 of folder support sees the recent observed messages of support, whatever their topic. For strict isolation, use separate folders.
Operators change topics from the chat
- Reset a topic
/new #nameclears that session./newclears the session of the default topic.- Pin or unpin a topic
- Send a bare
#nameto pin and a bare#to clear. - Inspect from the agent
- Call
inspect_sessionwith atopic. - Reset from the agent
- Call
reset_sessionwith the targetgroupFolder.
No arizuko subcommand sets or reads topics. Topics has the narrative, routing shows how a chat reaches a folder, and MCP tools lists every tool.