Zulip @mention Quick Start
This quick start verifies a real Zulip trigger for ChatRSS: one Zulip account sends a message mentioning a ChatRSS-managed watcher account; the watcher account polls Zulip with its own API key; ChatRSS normalizes the mention into an event, routes it, plans actions, and writes a ledger. The complete actor -> watcher -> worker -> action-bot reply case is documented in real-world cases.
What was verified
Environment: controlled Zulip service environment with public entry and private credential storage
Platform:
- Zulip URL:
https://zulip.public.wzhecnu.cn - Stream:
chatrss-quickstart - Topic:
trigger-router-action
Accounts:
| account | role |
|---|---|
watcher@example.invalid |
Sends the test message. |
watcher@example.invalid / ChatRSS Watcher Bot |
ChatRSS-managed watcher; polls Zulip and detects mentions. |
watcher@example.invalid / ChatRSS Agent Bot |
Action account used in the real reply case; external writes are not enabled by default. |
The watcher credentials and API key are stored only in the task-local secrets file on the host with mode 0600; no password or API key is stored in this repository.
Flow
ChatRSS Actor
-> sends a Zulip stream message mentioning @ChatRSS Watcher Bot
-> Watcher polls Zulip messages API using its own API key
-> Zulip message flags include mentioned
-> ChatRSS normalizes the message into TriggerEvent
-> Router/model stub decides act
-> Action planner produces dry-run actions
-> JSONL ledger records the whole chain
Verified message:
message_id: 16
permalink: https://zulip.public.wzhecnu.cn/#narrow/channel/chatrss-quickstart/topic/trigger-router-action/near/16
watcher_detected: true
mention_flag: true
Event envelope
{
"source": "zulip",
"connector": "zulip.messages",
"event_type": "community.mention.created",
"event_id": "zulip:message:16:mention:watcher@example.invalid",
"subject": {
"type": "zulip.message",
"stream": "chatrss-quickstart",
"topic": "trigger-router-action",
"message_id": 16
},
"raw": {
"mentioned": true,
"flags": ["mentioned"]
}
}
Router/action result
{
"decision": "act",
"model_used": "rule-router + deterministic model stub",
"actions": [
"internal.notify",
"agent.run",
"zulip.message.draft"
],
"requires_approval": true
}
All actions were dry-run/draft only:
| action | result | external write |
|---|---|---|
internal.notify |
DRY_RUN_OK |
false |
agent.run |
DRY_RUN_OK |
false |
zulip.message.draft |
DRY_RUN_OK |
false |
Validation artifacts
The validation script, ledger, JSON result, and report stay in a private task project. Public docs retain only non-sensitive message ids, event ids, action types, and workflow summaries; they do not expose hostnames, home paths, credential-file paths, or secret key names.
The reusable implementation target for ChatRSS is a future zulip.messages connector backed by a trigger job like:
id: zulip-watcher-mention
source: zulip
connector: zulip.messages
account: zulip-watcher
poll:
interval_seconds: 30
cursor: newest
filter:
mentioned: true
stream: chatrss-quickstart
context:
read_topic: true
max_messages: 50
actions:
- internal.notify
- agent.run
- zulip.message.draft
Complete real reply case
The minimum quick-start acceptance can stop at dry-run / draft actions. The verified full case went further and executed a real reply:
| Field | Value |
|---|---|
| Actor message | https://zulip.public.wzhecnu.cn/#narrow/channel/chatrss-quickstart/topic/trigger-router-action/near/20 |
| Reply message | https://zulip.public.wzhecnu.cn/#narrow/channel/chatrss-quickstart/topic/trigger-router-action/near/21 |
| Event id | zulip:message:20:mention:watcher@example.invalid |
| Action result | SENT external_write=true message_id=21 |
The actor asked a worker to analyze OpenAI Codex differences across a regular account, ChatGPT Plus, and ChatGPT Pro for coding usage. ChatRSS captured the mention, routed it to act, ran a bounded research worker, and used the action bot to reply in the same Zulip topic. The normalized event, route decision, and ledger order are documented in real-world cases.