# 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).
