# Report 0002 — Matrix-out: los bots de agents_and_robots hablan solo por unibus - **Fecha:** 07/06/2026 - **Autor:** Claude (Opus 4.8) - **Ámbito:** `~/DataProyects/Github/agents_and_robots` (Gitea `egutierrez/agents_and_robots`) y `projects/message_bus/apps/unibus` (`dataforge/unibus`) - **Estado:** done (con gaps acotados) ## Resumen Se arranca **todo Matrix (mautrix) de raíz** de `agents_and_robots` y se reconecta el core de agentes al bus `unibus` por el seam neutral `pkg/transport`. Modelo **"todo son rooms"** con **E2E** (`room.ModeMatrix`): el bot descubre por polling las rooms a las que lo invitan, se une, descifra y **responde en la misma room**. Resultado: el binario de agentes no contiene una sola línea de mautrix y compila sin `-tags goolm` ni `libolm`. Trabajo en ramas TBD, mergeado a master en ambos repos. Antecedente: la migración a mautrix v0.28 que se barajó NO era necesaria — era un parche para convivencia. Al quitar Matrix, el conflicto de versiones desaparece de raíz. ## Cambios ### unibus — descubrimiento de rooms (v0.4.0) | Cambio | Archivo | |---|---| | `GET /members/{endpoint}/rooms` (rooms de un endpoint + rol) | `pkg/membership/server.go` | | `Store.ListRoomsForEndpoint` (JOIN members+rooms) | `pkg/membership/store.go` | | `client.ListMyRooms() []RoomRef` | `pkg/client/client.go` | | Tests store + e2e cliente (descubrir room por invitación) | `pkg/membership/store_test.go`, `pkg/client/client_test.go` | El control plane es pull (no hay push de invitaciones); un peer recién invitado a una room cifrada la descubre por polling y luego hace `Join` + `Subscribe`. ### agents_and_robots — Matrix-out (4 commits) | Cambio | Detalle | |---|---| | **Borrado** | `shell/matrix/`, `tools/matrix/`, `cmd/verify/`, `cmd/agentctl/avatar.go`, dep `maunium.net/go/mautrix` | | **Config** | `MatrixCfg` → `BusCfg{NatsURL,CtrlURL,IdentityPath,Handle,CommandPrefix,Threads}`; loader valida `bus.*` | | **Interfaces** | `MatrixSender` → `Sender` en `effects/runner.go`, `cron/scheduler.go`, tool | | **Orquestador** | `shell/orchestration` despojado de mautrix (RoomScanner/scan/membership); aparcado, no se wirea | | **Transporte** | `shell/transportunibus` pasa al módulo principal (borrado el `go.mod` anidado) y se reescribe **room-based**: descubre (`ListMyRooms` polling) → `Join`+`Subscribe`; ignora sus propios frames; `IsDirectMsg` = room de 2 miembros (GET `/rooms/{id}/members` cacheado); `IsMention` = handle en el body; `Reply` publica en la room de origen. `busSender` adapta runner/cron/tools | | **Reconexión** | `runtime.go`/`robot.go` construyen el transport desde `BusCfg`, inyectan `busSender`, `Run` arranca `transport.Run(handleInbound)`; `sendReply` por el bus; nueva tool `bus_send` | | **Bots** | bloque `matrix:` → `bus:` en los 5 `agents/*/config.yaml` | ## Verificación ``` # unibus (Fase 0) $ cd projects/message_bus/apps/unibus && CGO_ENABLED=0 go test ./... ok pkg/membership ok pkg/client ok pkg/frame $ ./fn doctor services-spec | grep unibus # service vivo en la LAN OK unibus systemd-user 8470 /healthz ... $ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8470/members/x/rooms 200 # agents (Matrix-out), en master $ go build ./... # exit 0, SIN -tags goolm $ go vet ./... # exit 0 $ grep -rl maunium --include='*.go' . | grep -v /vendor/ # vacío $ grep -c maunium go.mod # 0 $ go test -count=1 ./shell/transportunibus/ ./pkg/transport/ ok shell/transportunibus (2.3s) ok pkg/transport ``` Test e2e clave `TestBotEchoesInEncryptedRoom` (room-based, E2E): un peer "humano" crea room `ModeMatrix`, invita al bot por su endpoint, publica; el bot descubre/join/subscribe, descifra y **responde en la misma room** con ancla `ReplyTo`; el humano recibe la respuesta descifrada. Suite completa agents: **25 paquetes ok**, 1 FAIL pre-existente ajeno (`shell/logger/TestCleanOldLogs`, dependiente de fecha contra fixtures de marzo). ## Gaps / pendientes 1. **Directorio de bots.** El frontend/humano necesita el `EndpointID` (+ claves públicas) del bot para invitarlo a una room cifrada. Hoy el bot lo expone (`Transport.BusEndpoint()`) y se invita a mano; un directorio formal (registro en membershipd o subject de anuncio) es fase posterior. 2. **member-count cacheado sin invalidación**: `IsDirectMsg` se fija la primera vez por room. Si una room de 2 crece, sigue marcada como DM. Aceptable v1 (la mención sigue disparando el LLM). 3. **Orquestador multi-bot aparcado**: el paquete compila sin mautrix pero no se wirea. Su reimplementación sobre `GET /rooms/{id}/members` + `bus.AgentMessage` queda para después. 4. **Presencia / typing / avatars**: no existen en unibus v1 → eliminados (no-op). Features nuevas del bus si se quieren. 5. **Smoke de launcher real**: el e2e valida transport + handler de eco; un arranque del `launcher` con un Agent real (LLM) contra el service vivo no se ha probado (requiere config LLM + invitar al bot). Recomendado como siguiente validación manual. 6. **Sin push**: master local en ambos repos; push vía `/full-git-push` del operador. 7. **Carrera git en unibus**: el agente de frontend trabaja en paralelo en el mismo repo unibus; Fase 0 se consolidó vigilando HEAD. agents_and_robots es repo aparte (sin carrera).