Переглянути джерело

Document the inbound gating order and its fail-open pitfall in the skill

Liv Klumpp 2 тижнів тому
батько
коміт
28bfda088c
1 змінених файлів з 15 додано та 0 видалено
  1. 15 0
      skills/chatto-platform-plugin/SKILL.md

+ 15 - 0
skills/chatto-platform-plugin/SKILL.md

@@ -56,6 +56,21 @@ Looks up the user by login or ID and posts into the DM room.
 - **A half-delivered send says so.** The split, the thread routing and the stop-at-first-failure live in `_deliver_chunks`; `send()` turns its result into a `SendResult` and `_post_chunks` into a refusal carrying `partial: true` plus the `message_ids` that went out, so a retry repeats only what is missing. **Add a new send path by calling `_deliver_chunks`, not by writing a new loop** — two copies of the routing rules drift, and `send()` has to keep marking its own output seen (`on_posted`) so the realtime echo never comes back as input.
 - **Verify in BOTH contexts:** a gateway-session `tool_call` is not enough — also fire a `cronjob_manage action='run'` test when the tool will run from cron, because cron sessions exercise the config-fallback tier gateway sessions never hit. Debug order: `tool_result_contract` error → contract bug (async/return type); `AttributeError` on the client → wrong stub/service name; `permission_denied` → server-side limit.
 
+## Inbound gating — debugging "the bot answered a message it should have ignored"
+
+All admission gates live in `_admit_and_build` (adapter.py), shared by the posted and the edited path, and run in order: read-only-room drop, user auth, `REQUIRE_MENTION` drop of unaddressed messages, then the OPEN-room "someone else" check (`_mentions_someone_else` → 🫥 reaction, no dispatch). DMs skip every mention gate.
+
+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.
+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.
+
+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.
+- **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
 
 1. **Read the repo's `AGENTS.md` first** — it is the binding rulebook for all work on this repo: editing, linting/type-checking gates, shell-script checks, typed access, overriding, capabilities, README discipline. This skill does not repeat those rules.