fix: agent can't see Gitea webhooks in Mattermost (bot-to-bot limitation)
All checks were successful
check / check (push) Successful in 11s

Mattermost hides bot messages from other bots to prevent loops.
Document why the notification poller exists as a workaround.
This commit is contained in:
clawbot 2026-02-28 02:43:35 -08:00
parent a6cd3e5997
commit 4f68638a28

View File

@ -226,13 +226,26 @@ Docker build, and branch protection enforces CI. No single point of failure.
Every repo has a Gitea webhook that sends all activity (pushes, PRs, issues, Every repo has a Gitea webhook that sends all activity (pushes, PRs, issues,
comments, reviews, CI status) to a channel in our self-hosted comments, reviews, CI status) to a channel in our self-hosted
[Mattermost](https://mattermost.com) instance. This creates a real-time feed [Mattermost](https://mattermost.com) instance. This creates a real-time feed
where everyone — human and agent — can see what's happening across all projects where the human can see what's happening across all projects without checking
without cross-communication overhead. Gitea's notification inbox.
The agent also has its own Mattermost bot user (`@claw`), separate from the **Important caveat:** The agent can't see this feed directly. Gitea's webhook
human. This means: messages arrive in Mattermost as a "bot" integration user. Mattermost
deliberately hides bot messages from other bot users to prevent infinite
bot-to-bot loops. This means the agent's Mattermost bot account is blind to the
Gitea webhook feed, even though it's posted in a channel the agent has access
to.
- The agent posts status updates to dedicated channels (`#git` for Gitea work, This is why we built the [notification poller](#the-notification-poller) — a
separate Python script that polls Gitea's notification API directly, bypassing
Mattermost entirely. The human sees Gitea activity via the Mattermost webhook
feed; the agent sees it via the API poller. Same events, different delivery
paths, because of a Mattermost platform limitation.
The agent has its own Mattermost bot user (`@claw`), separate from the human.
This means:
- The agent posts status updates to dedicated channels (`#git` for work status,
`#claw` for general work narration) `#claw` for general work narration)
- The human's DMs stay clean — only direct alerts and responses - The human's DMs stay clean — only direct alerts and responses
- In group channels, it's clear who said what - In group channels, it's clear who said what
@ -243,16 +256,33 @@ human. This means:
A practical setup: A practical setup:
- **#git** — Real-time Gitea webhook feed (all repos) + agent's work status - **#git** — Real-time Gitea webhook feed (all repos) + agent's work status
updates. Everyone sees commits, PRs, reviews, CI results as they happen. updates. The human sees commits, PRs, reviews, CI results as they happen. (The
agent posts here but can't read the webhook messages — see caveat above.)
- **#claw** — Agent's internal work narration. Useful for debugging what the - **#claw** — Agent's internal work narration. Useful for debugging what the
agent is doing, but notifications muted so it doesn't disturb anyone. agent is doing, but notifications muted so it doesn't disturb anyone.
- **DM with agent** — Private conversation, sitreps, sensitive commands - **DM with agent** — Private conversation, sitreps, sensitive commands
- **Project-specific channels** — For coordination with external collaborators - **Project-specific channels** — For coordination with external collaborators
The webhook feed in #git means nobody needs to check Gitea's notification inbox ### The Notification Poller
manually. PRs, reviews, and CI results flow into a channel that's always open.
The agent monitors the same feed (via its notification poller) and can react to Because the agent can't see Gitea webhooks in Mattermost (bot-to-bot visibility
events in near-realtime. issue), we built a lightweight Python script that polls the Gitea notifications
API every 2 seconds and wakes the agent via OpenClaw's `/hooks/wake` endpoint
when new notifications arrive.
Key design decisions:
- **The poller never marks notifications as read.** That's the agent's job after
processing. Prevents the poller and agent from racing.
- **Tracks notification IDs, not counts.** Only fires on genuinely new
notifications, not re-reads of existing ones.
- **The wake message tells the agent to route output to Gitea/Mattermost, not
DM.** Prevents chatty notification processing from disturbing the human.
- **Zero dependencies.** Python stdlib only (`urllib`, `json`, `time`). Runs
anywhere.
Full source code is available in
[OPENCLAW_TRICKS.md](OPENCLAW_TRICKS.md#the-gitea-notification-poller).
## CI: Gitea Actions ## CI: Gitea Actions