Explorar el Código

Correct the gating skill: the path it documents no longer exists

d6b8ba7 removed admin_get_member from handle resolution, so the debug line
"could not resolve handle @x: <error>" it logged is gone with it — and 28bfda0, in
the same series, told readers to grep for exactly that line and to treat
permission_denied as the root cause of barging in. Both were true when written and
are stale on arrival: a maintainer following those steps finds nothing in the log
and draws the wrong conclusion.

What replaces it is worth stating, because the failure mode changed shape. The
check is now a pure lookup in _user_cache, so it cannot fail per-request; what
goes wrong is a cold or thin roster projection, and an unknown handle still
resolves to "not a mention". So the diagnosis is "what does the roster know about
this room", not "was a call rejected". Kept the reason the admin directory is out
of bounds — its permission_denied failed every lookup in the field, and fail-open
turned that into "answer every @colleague message" — since that is what stops
someone re-adding the lookup.

Verified while here: no admin call remains in the adapter at all. The roster
projection it now reads comes from list_room_members / batch_get_room_members,
which a room member may call, so a bot without admin rights does fill the cache
the resolution depends on.
Paul Klumpp hace 2 semanas
padre
commit
0c01ae4db6
Se han modificado 1 ficheros con 3 adiciones y 3 borrados
  1. 3 3
      skills/chatto-platform-plugin/SKILL.md

+ 3 - 3
skills/chatto-platform-plugin/SKILL.md

@@ -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