|
|
@@ -62,13 +62,13 @@ All admission gates live in `_admit_and_build` (adapter.py), shared by the poste
|
|
|
|
|
|
Procedure when the bot answered a channel message it should not have:
|
|
|
|
|
|
-1. Read `~/.hermes/logs/agent.log` (DEBUG) around the message timestamp — the adapter logs every decision: `message_body: … room_kind:`, `could not resolve handle @x: <error>`, `acknowledging only`, `dispatching message to Hermes`. `~/.hermes/logs/gateway.log` (INFO) carries the summarized inbound line with room and session key.
|
|
|
+1. Read `~/.hermes/logs/agent.log` (DEBUG) around the message timestamp — the adapter logs the decision: `message_body: … room_kind:`, then either `message addresses someone else, acknowledging only` or `dispatching message to Hermes`. `~/.hermes/logs/gateway.log` (INFO) carries the summarized inbound line with room and session key. Handle resolution logs nothing: it is a pure cache lookup, so its silence is not a signal either way.
|
|
|
2. Establish the room's policy before blaming the gates: `gateway.platforms.chatto-platform.extra.require_mention_rooms` / `optional_mention_rooms` in `~/.hermes/config.yaml`, and the connect-time log line `currently joined in N room(s)`, which tags each room `[dm]` / `[on-mention]` / `[every-message]` / `[read-only]`.
|
|
|
-3. In an `[every-message]` (OPEN) room the only silencing gate is `_mentions_someone_else`, which confirms each @handle through `_handle_belongs_to_a_user` → `admin_get_member`. Treat fail-open resolution as a root-cause candidate: `permission_denied` resolves the handle to False ("not a mention"), so every foreign mention passes — and a bot without admin rights fails this systematically, not sporadically.
|
|
|
+3. In an `[every-message]` (OPEN) room the only silencing gate is `_mentions_someone_else`, which confirms each @handle through `_handle_belongs_to_a_user`. That check reads **local knowledge only** — `_user_cache`, filled by the room roster fetched for the very same dispatch — and makes no API call, so it cannot fail per-request. Its failure mode is a *cold or thin cache*, not a permission error: an unresolved handle reads as "not a mention", so a handle nobody has ever seen passes. If foreign mentions are getting through, ask what the roster projection knows about that room — not whether a call was rejected.
|
|
|
|
|
|
Pitfalls:
|
|
|
|
|
|
-- **Fail-open gates degrade silently.** An unresolved handle reads as "not a mention", so the bot barges in on every @colleague message with no log line louder than DEBUG. Before defaulting a handle to False, prefer local knowledge the adapter already holds (`_user_cache`, filled by the room roster fetched for the very same dispatch) over a directory call — and cache the failure mode, otherwise each message re-pays the doomed API call.
|
|
|
+- **Fail-open gates degrade silently.** An unresolved handle reads as "not a mention", so the bot barges in on every @colleague message with no log line louder than DEBUG. The fix is a fuller local cache, never a per-handle lookup: an admin-directory call (`admin_get_member`) needs permissions a bot account must not hold, and its `permission_denied` failed *every* lookup in the field, which fail-open turned into "answer every @colleague message". Prefer what the roster projection already holds, and keep it fed — the OPEN admission path fetches it precisely so this check has something to read.
|
|
|
- **Mentions are extracted from body text** (`ChattoConstants.MENTION_RE`) outside code fences and inline code spans, matching the upstream web frontend — a handle quoted in code does not gate, and case-insensitivity applies throughout.
|
|
|
|
|
|
## Development workflow
|