# 0030 — Separacion Robot vs Agente

## Objetivo

Crear un tipo `Robot` como runtime ligero para bots que solo responden comandos, sin LLM, reglas, memoria ni tools. Distinguir en config entre `type: robot` y `type: agent` para que el launcher sepa que runtime instanciar.

## Contexto

- Actualmente todos los bots usan el mismo `Agent` struct (1,182 lineas) con 25+ subsistemas
- Un bot de comandos simples (ej: `!deploy prod`, `!status`) no necesita LLM, memoria, knowledge, skills, sanitizacion, ni tool-use
- Si `llm.primary.provider` esta vacio, `runtime.go` loguea "running as command-only bot" pero sigue inicializando todo el subsistema
- No hay forma idiomatica de crear un bot simple sin arrastrar toda la complejidad
- Ejemplos de robots: bot de deploys, bot de health checks, bot de notificaciones, bot de CI/CD

## Arquitectura

```
agents/robot.go          NEW → Robot struct (~150 lineas): Matrix + Commands
agents/robot_test.go     NEW → tests del runtime minimo
agents/types.go          NEW → interfaz comun Runner { Run(ctx), Stop(), RegisterCommand() }
cmd/launcher/main.go     → detectar type: robot y crear Robot en vez de Agent
internal/config/schema.go → añadir campo Agent.Type ("robot" | "agent")
```

### Patron pure core / impure shell

- `pkg/` — sin cambios (el Robot no usa decision engine ni reglas)
- `shell/matrix/` — sin cambios (el Robot reutiliza el mismo cliente Matrix)
- `agents/robot.go` — impuro (tiene Matrix client), pero minimo
- `agents/runtime.go` — sin cambios (Agent sigue igual)

## Tareas

### Fase 1: Definir interfaz comun

- [ ] **1.1** Crear `agents/types.go` con interfaz `Runner`:
  ```go
  type Runner interface {
      Run(ctx context.Context) error
      Stop()
      RegisterCommand(spec command.Spec, handler CommandHandler)
  }
  ```
- [ ] **1.2** Verificar que `Agent` ya satisface `Runner` (o adaptar)

### Fase 2: Implementar Robot

- [ ] **2.1** Crear `agents/robot.go` con struct `Robot`:
  - `matrix *matrix.Client`
  - `commands *command.Registry` (built-ins + custom)
  - `logger *slog.Logger`
  - `config config.AgentConfig`
- [ ] **2.2** Implementar `NewRobot(cfg, logger)` — solo inicializa Matrix + commands
- [ ] **2.3** Implementar `Run()` — sync loop que solo despacha comandos
- [ ] **2.4** Implementar `Stop()` — cierra Matrix client
- [ ] **2.5** Implementar `RegisterCommand()` — registra comando custom
- [ ] **2.6** En `handleEvent()`: si no es comando, ignorar silenciosamente (no hay LLM)

### Fase 3: Config y launcher

- [ ] **3.1** Añadir campo `Type string` a `AgentCfg` en schema.go (default: "agent")
- [ ] **3.2** En launcher: si `cfg.Agent.Type == "robot"`, crear `NewRobot()` en vez de `New()`
- [ ] **3.3** El launcher usa la interfaz `Runner` para manejar ambos tipos uniformemente

### Fase 4: Template y scaffolding

- [ ] **4.1** Crear `agents/_template_robot/` con config minimo para robots
- [ ] **4.2** Config de robot ejemplo (~20 lineas):
  ```yaml
  agent:
    id: deploy-bot
    type: robot
    description: "Bot de deploys"
  personality:
    prefix: "🤖"
  matrix:
    threads:
      enabled: true
  ```
- [ ] **4.3** Actualizar `dev-scripts/agent/create-full.sh` para aceptar flag `--robot`

### Fase 5: Tests

- [ ] **5.1** Test: Robot responde a `!help` con lista de comandos
- [ ] **5.2** Test: Robot responde a `!ping` con pong
- [ ] **5.3** Test: Robot ignora mensajes normales (no es error, simplemente no responde)
- [ ] **5.4** Test: Robot con comando custom registrado lo ejecuta
- [ ] **5.5** Test: `Runner` interfaz es satisfecha por ambos `Agent` y `Robot`

### Fase 6: Documentacion

- [ ] **6.1** Actualizar `CLAUDE.md` con seccion Robot vs Agent
- [ ] **6.2** Actualizar `.claude/rules/create_agent.md` mencionando la opcion robot
- [ ] **6.3** Añadir tabla comparativa en docs

---

## Ejemplo de uso

```yaml
# agents/deploy-bot/config.yaml
agent:
  id: deploy-bot
  type: robot
  description: "Bot de deploys con comandos directos"

personality:
  prefix: "🚀"

matrix:
  homeserver: ${MATRIX_HOMESERVER}
  user_id: "@deploy-bot:matrix-af2f3d.organic-machine.com"
  access_token_env: MATRIX_TOKEN_DEPLOY_BOT
```

```go
// agents/deploy-bot/commands.go
package deploy

func Commands() []agents.CommandEntry {
    return []agents.CommandEntry{
        {
            Spec: command.Spec{Name: "deploy", Usage: "!deploy <env>"},
            Handler: func(ctx context.Context, msg decision.MessageContext) string {
                return fmt.Sprintf("Deploying to %s...", msg.Args[0])
            },
        },
    }
}
```

Interaccion en Element:
```
Usuario: !deploy staging
Bot:     🚀 Deploying to staging...

Usuario: hola bot
Bot:     (silencio — no tiene LLM)
```

## Decisiones de diseno

- **Interfaz `Runner`**: permite al launcher tratar robots y agentes uniformemente sin type switches
- **Robot NO tiene reglas**: las reglas son para routing inteligente. Un robot solo hace dispatch de comandos
- **Robot NO tiene memory/knowledge/skills**: mantener el runtime lo mas ligero posible
- **Config minimo**: un robot funcional se define en ~20 lineas de YAML
- **Silencio ante mensajes normales**: un robot no responde "no entiendo", simplemente ignora. Los comandos tienen `!help` para descubrirse

## Prerequisitos

- Ninguno (puede hacerse independiente)
- Se beneficia de 0026 (split runtime) pero no lo requiere

## Riesgos

- **Duplicacion**: Robot y Agent comparten logica de Matrix, commands, lifecycle. Mitigacion: reutilizar `shell/matrix/` y `pkg/command/` sin duplicar
- **Scope creep**: tentacion de añadir "un poquito de LLM" al Robot. Mitigacion: la linea es clara — si necesita LLM, es un Agent
