Forráskód Böngészése

docs: slim plugin skill — drop API quirks + protobuf split + edit/commit steps

AGENTS.md already governs repo editing (symlink checks, lint gates,
vendoring); chattolib import mechanics and stub naming belong to the code,
not the agent-facing skill. Skill now covers only: tool usage, cron delivery
patterns, the plugin tool contract, and the gateway-restart workflow.
Liv Klumpp 2 hete
szülő
commit
304aebba89
1 módosított fájl, 11 hozzáadás és 24 törlés
  1. 11 24
      skills/chatto-platform-plugin/SKILL.md

+ 11 - 24
skills/chatto-platform-plugin/SKILL.md

@@ -1,7 +1,7 @@
 ---
 name: chatto-platform-plugin
-description: "Use when working with the Hermes Chatto platform plugin: its tools, the Chatto API quirks, or extending the plugin."
-version: 1.1.0
+description: "Use when working with the Hermes Chatto platform plugin: its tools, cron delivery patterns, or extending the plugin."
+version: 1.2.0
 author: Paul Klumpp
 license: MIT
 metadata:
@@ -13,7 +13,7 @@ metadata:
 
 ## When to Use
 
-Using or developing/debugging the Chatto gateway plugin: its plugin tools (`chatto_send_dm`, `chatto_send_room`, `chatto_list_rooms`), the Chatto API, the vendored chattolib, or adding new tools to the plugin.
+Using or developing/debugging the Chatto gateway plugin: its plugin tools (`chatto_send_dm`, `chatto_send_room`, `chatto_list_rooms`) or adding new tools to the plugin. Repo-internal rules (editing, linting, vendoring) live in the plugin root's `AGENTS.md` — see Development workflow.
 
 ## The plugin tools and when to use them
 
@@ -21,7 +21,7 @@ Using or developing/debugging the Chatto gateway plugin: its plugin tools (`chat
 
 Lists every room the bot is a member of: `room_id`, `name`, `kind` (channel/DM), `threading_mode`, `archived`. Optional `name` filters case-insensitively by substring (unnamed DM rooms are skipped when a filter is set; without a filter they appear with an empty name and label `(DM room <id>)`).
 
-**Primary use case: cron `deliver` targets.** The Hermes cron `deliver` field takes only `platform:chat_id` strings (e.g. `chatto-platform:RbmwiHk4vs9a3Ql`) and does NO name resolution. When a scheduled job's report should go to a channel, call this tool first, take the `room_id`, and use it as the `chat_id` part. Never trust chat names in session/origin metadata as room IDs — resolve live.
+**Primary use case: cron `deliver` targets.** The Hermes cron `deliver` field takes only `platform:chat_id` strings and does NO name resolution. When a scheduled job's report should go to a channel, call this tool first, take the `room_id`, and use it as the `chat_id` part. Never trust chat names in session/origin metadata as room IDs — resolve live.
 
 ### chatto_send_room(room_id|room_name, message, create_thread?, thread_root_event_id?) — post into a channel
 
@@ -31,7 +31,7 @@ Resolves the room case-insensitively by name OR verifies the ID, then posts. Wor
 - `thread_root_event_id` posts INSIDE an existing thread. Mutually exclusive with `create_thread`.
 - Response carries `thread_root_event_id` + `created_thread`.
 
-**Mentions instead of DMs:** to notify a user inside a channel, mention them in the message TEXT (`@login, hier ist …`) — Chatto renders room-text mentions. A bot cannot deliver a personal notification any other way (see DM limitation below).
+**Mentions instead of DMs:** to notify a user inside a channel, mention them in the message TEXT (`@login, hier ist …`) — Chatto renders room-text mentions. A bot cannot deliver a personal notification any other way (see the DM limitation below).
 
 ### chatto_send_dm(user_login|user_id, message) — DM a user
 
@@ -49,26 +49,13 @@ Looks up the user by login or ID and posts into the DM room.
 
 - **Async handlers need `is_async=True`** — the default is sync; an `async def` handler registered without it returns an unawaited coroutine and the dispatcher fails with `tool_result_contract: unsupported result type: coroutine` (`tools/registry.py: dispatch()`).
 - **Handlers must return JSON strings** (`json.dumps({...})`), never plain dicts — `_normalize_handler_result` accepts only str or the multimodal envelope.
-- **Config access: never read `os.environ` in handlers.** `.env` credentials like `CHATTO_TOKEN` are profile-scoped and NOT visible in cron fires' os.environ, and `hermes_validate_config` never runs in a fresh cron session. Resolve config through `_chatto_config_for_tools()` in adapter.py: (1) module-level `_resolved_chato_config` set by `hermes_validate_config` in a running gateway; (2) empty `PlatformConfig(enabled=True, extra={})` wrapped in `ChattoConfiguration`, re-filled from `build_profile_secret_scope(get_hermes_home())` (`agent.secret_scope`) — re-reads the profile's own `.env`, works in gateway, cron and CLI alike; (3) clean error. Do NOT use `load_gateway_config().platforms[name]` as fallback (returns None in fresh processes) and do NOT leak a `token` parameter into the tool schema.
+- **Config access: never read `os.environ` in handlers.** `.env` credentials like `CHATTO_TOKEN` are profile-scoped and NOT visible in cron fires' os.environ, and `hermes_validate_config` never runs in a fresh cron session. Resolve config through `_chatto_config_for_tools()` in adapter.py: (1) module-level `_resolved_chatto_config` set by `hermes_validate_config` in a running gateway; (2) empty `PlatformConfig(enabled=True, extra={})` wrapped in `ChattoConfiguration`, re-filled from `build_profile_secret_scope(get_hermes_home())` (`agent.secret_scope`) — re-reads the profile's own `.env`, works in gateway, cron and CLI alike; (3) clean error. Do NOT use `load_gateway_config().platforms[name]` as fallback (returns None in fresh processes) and do NOT leak a `token` parameter into the tool schema.
 - **Reuse `_chatto_tool_client(args, cfg)`** for client construction in new handlers instead of re-inlining token/base_url resolution.
-- **A field resolving to its declared default is a HOLE, not a value**: `base_url` defaults to `chat.chatto.run` — non-empty but wrong when the profile `.env` says otherwise. `ConfigValue.explicit` (commit 8b95ae0) distinguishes user-set values from default leakage; the `.env` override tier checks `explicit`.
-- **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 client → wrong stub/service name; `permission_denied` → server-side limit.
-
-## Chatto API quirks
-
-- **protoc splits consecutive capitals in RPC names**: `StartDM` → stub method `start_d_m` (`_pb/chatto/api/v1/*_connect.py`). Always grep the generated stub for the exact method name — never guess snake_case from the RPC.
-- **Bot tokens have no admin rights**: `admin_get_member` etc. return `permission_denied`. Resolve users by login via `list_room_members(room_id, search=login)` + `batch_get_room_members(room_id, ids)` across the bot's rooms, matching `user.login` case-insensitively.
-- `post_message` lives on the messages service (`client.post_message`), not the rooms stub.
-- **There is no "create thread" RPC** — a thread is created by posting a root message, then posting a reply to the same room with `thread_root_event_id=<root id>`; verify the reply's own `thread_root_event_id` echoes the root.
-- **chattolib client methods are async** (`list_rooms()`, `post_message()` … return coroutines); `list_rooms()` yields `RoomWithViewerState` wrappers — `id`, `name`, `kind` sit on the nested `.room` attribute, NOT top-level.
+- **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.
 
 ## Development workflow
 
-1. **Read the repo's `AGENTS.md` first** — it is the rulebook for developing this plugin (contradiction-free rule, vendored-chattolib-only policy, architecture references, agent conduct). It ships in the plugin root next to this skill and is binding for all work on the repo.
-2. Edit `adapter.py` or vendored chattolib (`vendor/common/chattolib/`, platform-split via `vendor_path.py`).
-3. Syntax gate: `python3 -m py_compile adapter.py` before every commit.
-4. Commit + push on the feature branch — the plugin directory IS a git repo.
-5. **Gateway restart is REQUIRED after plugin code changes** — the running session keeps the old module in memory. Ask the user to restart, then verify via `tool_call`.
-6. **Before requesting a restart, verify config-dependent fixes in an isolated subprocess** simulating the cron scope: pop the relevant env vars (`CHATTO_TOKEN`, `CHATTO_BASE_URL`), `sys.path.insert` the Hermes kernel and plugin dir, set `HERMES_HOME`, call the handler directly. A green isolated test proves the cron path before burning a restart cycle.
-7. **Importing vendored chattolib standalone: honor the protobuf split.** The vendored `_pb` gencode (7.35.x) needs a protobuf runtime at least that new; Hermes venvs can ship an older one (6.x) → `VersionError: gencode 7.x runtime 6.x`. Fix: fresh subprocess with `vendor/platform/<tag>` PREPENDED to sys.path and `vendor/common` APPENDED (same order as `vendor_path.setup_vendor_path()`). Platform tags: `linux-x86_64`, `linux-aarch64`, `macos-arm64`. Read `CHATTO_TOKEN`/`CHATTO_BASE_URL` from the profile `.env` for standalone tests. In a persistent kernel an already-imported old protobuf stays in `sys.modules` — always go through a fresh subprocess.
-8. **Bundled skills:** register additional skills in `register(ctx)` via `ctx.register_skill(name, path_to_SKILL.md)` — see the `skills/` directory of this plugin.
+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.
+2. **Gateway restart is REQUIRED after plugin code changes** — the running Hermes session keeps the old module in memory. Ask the user to restart, then verify via `tool_call`.
+3. **Before requesting a restart, verify config-dependent fixes in an isolated subprocess** simulating the cron scope: pop the relevant env vars (`CHATTO_TOKEN`, `CHATTO_BASE_URL`), `sys.path.insert` the Hermes kernel and plugin dir, set `HERMES_HOME`, call the handler directly. A green isolated test proves the cron path before burning a restart cycle.
+4. **Bundled skills:** register additional skills in `register(ctx)` via `ctx.register_skill(name, path_to_SKILL.md)` — see the `skills/` directory of this plugin.