# 0028 — Desacoplar launcher del registro estatico de agentes ## Objetivo Eliminar la necesidad de editar `cmd/launcher/main.go` cada vez que se añade un agente. Reemplazar el `rulesRegistry` hard-coded con auto-discovery basado en la convencion de directorios. ## Contexto - Actualmente `cmd/launcher/main.go` importa cada paquete de agente explicitamente: ```go import ( assistantagent "github.com/enmanuel/agents/agents/assistant-bot" asistente2agent "github.com/enmanuel/agents/agents/asistente-2" ) var rulesRegistry = map[string]func() []decision.Rule{...} ``` - Cada agente nuevo requiere: añadir import + añadir entrada al map + recompilar - El script `dev-scripts/agent/new-agent.sh` ya modifica el launcher automaticamente, pero es fragil (sed sobre codigo Go) - Contradiccion: el launcher hace glob de `agents/*/config.yaml` para descubrir configs, pero luego necesita imports estaticos para las reglas ## Arquitectura ``` agents/registry.go NEW → registro global de reglas (init-based) agents//agent.go → cada agente se auto-registra via init() cmd/launcher/main.go → eliminar rulesRegistry, usar agents.GetRules(id) ``` ### Patron pure core / impure shell - `pkg/` — sin cambios - `shell/` — sin cambios - `agents/` — nuevo registry global + init() en cada agente - `cmd/launcher/` — simplificacion ## Tareas ### Fase 1: Crear registry de reglas - [ ] **1.1** Crear `agents/registry.go` con `Register(id, rulesFn)` y `GetRules(id)` - [ ] **1.2** Usar sync.Mutex o sync.Map para seguridad en init() ### Fase 2: Migrar agentes a auto-registro - [ ] **2.1** En `agents/assistant-bot/agent.go` añadir `func init() { agents.Register("assistant-bot", Rules) }` - [ ] **2.2** Repetir para `asistente-2` y `meteorologo` - [ ] **2.3** Actualizar `agents/_template/agent.go` con el patron init() ### Fase 3: Simplificar launcher - [ ] **3.1** Eliminar imports explicitos de agentes en `cmd/launcher/main.go` - [ ] **3.2** Añadir blank import: `_ "github.com/enmanuel/agents/agents/assistant-bot"` (etc.) - [ ] **3.3** Reemplazar `rulesRegistry[id]` con `agents.GetRules(id)` - [ ] **3.4** Si no hay reglas registradas para un agent id, log warning y usar reglas vacias (command-only bot) ### Fase 4: Actualizar scripts - [ ] **4.1** Simplificar `dev-scripts/agent/new-agent.sh` — ya no necesita editar el map, solo añadir blank import - [ ] **4.2** Actualizar `.claude/rules/create_agent.md` con el nuevo patron ### Fase 5: Tests - [ ] **5.1** Test para `agents/registry.go` (register, get, get-missing) - [ ] **5.2** `go build -tags goolm ./...` compila - [ ] **5.3** `go test -tags goolm ./...` pasa ### Fase 6: Cleanup - [ ] **6.1** Actualizar `CLAUDE.md` seccion sobre registro en launcher - [ ] **6.2** Eliminar codigo muerto del launcher --- ## Ejemplo de uso Antes (crear agente): ```go // cmd/launcher/main.go — editar manualmente import newagent "github.com/enmanuel/agents/agents/new-bot" var rulesRegistry = map[string]func() []decision.Rule{ "new-bot": newagent.Rules, // añadir esta linea } ``` Despues: ```go // agents/new-bot/agent.go — auto-registro func init() { agents.Register("new-bot", Rules) } // cmd/launcher/main.go — solo blank import import _ "github.com/enmanuel/agents/agents/new-bot" ``` ## Decisiones de diseno - **init() + blank import**: patron estandar en Go (database/sql drivers, image codecs). Simple y familiar - **Blank imports en launcher**: siguen siendo estaticos en el codigo, pero son una linea trivial sin logica. El script de scaffolding puede añadirla sin riesgo de romper sintaxis Go - **No plugin system dinamico**: Go no tiene plugins portables. init() es el mecanismo idomatic ## Prerequisitos - Ninguno (puede hacerse independiente de otros issues) ## Riesgos - **Orden de init()**: Go garantiza init() dentro de un paquete, pero no entre paquetes. Mitigacion: el registro es un map simple, el orden no importa - **Olvidar blank import**: si no se añade el blank import, el agente no se registra y el launcher lo trata como command-only. Mitigacion: el script de scaffolding lo añade automaticamente