CASO DE ESTUDIO · CONECTA
Cinco productos, un equipo pequeño y un flujo de ingeniería con agentes
Cómo construí el ecosistema digital de Conecta y el sistema con el que se desarrolla: un plugin de Claude Code que lleva cada cambio del ticket a producción, con un evaluador que no es quien implementa.
La operación de Conecta involucra a gerencia, al equipo comercial y a técnicos en campo, y cada uno necesita una herramienta distinta: un CRM para leads y PQRS, una app para el trabajo en campo, un formulario público para agendar instalaciones y un sitio web para captar clientes. Hoy son cinco productos en producción, y una feature típica toca dos o tres de ellos a la vez.
Construir esas aplicaciones era la parte fácil. Lo difícil era que un equipo pequeño pudiera cambiarlas todos los días sin romper las demás, con datos personales reales de por medio1 y con gerencia —que es quien pide los cambios— fuera del ciclo de desarrollo.
Este artículo cuenta las dos cosas que construí para resolverlo. La primera es una arquitectura donde un único backend es la fuente de verdad del contrato y mergear es desplegar. La segunda es un flujo de trabajo con agentes de IA, empaquetado como plugins de Claude Code, que recibió 48 pull requests en sus primeros cuatro meses. Cuento qué decisiones tomé, qué no funcionó a la primera y qué tuve que quitar para que el sistema siguiera siendo útil.
Una base de datos y un contrato
Cuando un cambio cruza varios repos, el error más caro es el que no aparece en el editor: el backend renombra un campo, el frontend sigue compilando y el fallo llega a producción. Decidí que el dominio entero viviera en una sola API —Express 5 con Prisma 7 sobre Postgres en Supabase— y que ningún frontend hablara con la base de datos directamente.
El backend es la única fuente de verdad del contrato. Cada frontend sincroniza sus tipos y esquemas desde ahí, y una auditoría compara ambos lados antes de cada pull request: si hay drift, el PR no sale. Las entradas se validan con Zod y los logs son estructurados con Pino, así que un error en producción se puede seguir de un servicio a otro.
Las migraciones son la parte que no se puede deshacer con un revert, así que tienen su propio carril. Se escriben y se prueban contra un Postgres desechable, se revisan como SQL y solo llegan a producción por un workflow con aprobación manual y un backup previo.
Mergear es desplegar
Los cinco repos se despliegan solos al mergear: la API en Vercel y los frontends en Hostinger detrás de una CDN. Hay dos entornos con nombre propio. Ranger es desarrollo, se alimenta de la rama develop y es donde gerencia prueba. Endurance es producción y se alimenta de main. Todos los repos usan gitflow y Conventional Commits.
Cada repo corre en CI typecheck, lint, build y tests. Lo que no es automático está marcado: las migraciones de producción y cualquier cambio irreversible pasan por una aprobación explícita. Al terminar un release, un back-merge lleva main de vuelta a develop. La primera versión decidía si hacía falta contando commits, y abría PRs vacíos después de cada merge; ahora lo decide por contenido y solo abre el PR cuando mergear main cambiaría algo en develop.
Un flujo de ingeniería con agentes
Los agentes de IA aceleran la escritura de código, pero sin estructura tienen tres problemas que se notan enseguida en un ecosistema de varios repos: se aprueban a sí mismos, olvidan las convenciones de cada proyecto y gastan contexto en lo que no importa. Para eso construí un marketplace propio con tres plugins de Claude Code: conecta fija convenciones y calidad, conecta-flow lleva una feature de la descripción a producción y conecta-sage cubre la operación y el on-call.
Del ticket a producción
Toda feature sigue el mismo camino, sin importar cuántos repos toque. Se especifica con criterios de aceptación verificables, se implementa con TDD —backend primero, luego cada frontend—, la revisa un evaluador independiente, pasa una auditoría de seguridad y se promueve por entornos.
- 01 DEFINIR
/ticketGerencia describe el cambio y cómo sabrá que sirve/planContrato de API y criterios de aceptación por repo/preflightRevisión adversarial del spec
- 02 CONSTRUIR
/executeTDD: backend primero, luego cada frontend/fixDiagnóstico hasta dejar todo en verde
- 03 VERIFICAR
/reviewEvaluador independiente, criterio por criterio/securityAutorización, PII, secretos y RLS
- 04 ENTREGAR
/prSolo si todos los criterios están verificados/promotePruebas → aprobación → producción
Doce de los comandos los teclea siempre una persona. /promote, /rollback, /hotfix, /pr y los demás que tienen efectos secundarios —commits, PRs, tickets, producción— llevan disable-model-invocation: el agente puede recomendarlos, pero no dispararlos solo. En un comando que toca producción, que el modelo pudiera invocarlo por su cuenta no era una capacidad, era un riesgo.
Quien implementa no se aprueba a sí mismo
El fallo más común que veía en los agentes no era escribir mal el código, sino declararlo terminado sin comprobarlo. Por eso cada criterio de aceptación tiene un estado durable en el repo, y solo un evaluador con contexto fresco puede marcarlo como verificado. El evaluador no ve la conversación en la que se implementó: lee el spec y el código, corre los tests y dictamina criterio por criterio.
El trabajo se reparte entre cuatro subagentes, cada uno con el modelo elegido según lo que hace: uno implementa el backend con TDD, otro implementa cada frontend con sus propias convenciones, otro revisa y otro audita seguridad —autorización, IDOR, datos personales, secretos que acaban en el bundle del navegador y las políticas RLS de Supabase—. La revisión y la seguridad usan el modelo más capaz, con el porqué escrito al lado; los comandos que solo formatean la salida de gh usan el más barato.
Gerencia dentro del ciclo
Antes, quien pedía un cambio no tenía una forma clara de describirlo, probarlo ni aprobarlo antes de que llegara a los clientes. Ahora gerencia escribe el ticket en Jira en lenguaje de negocio, con una línea que empieza por «sabré que sirve cuando…», y el flujo lo refina en criterios técnicos sin tocar lo que escribieron. La aprobación ocurre probando la feature en Ranger, no leyendo código, y /promote no pasa a producción hasta que el ticket está aprobado.
Una regla que tuve que escribir explícitamente: Jira nunca bloquea el desarrollo. Si el token no está configurado, el tablero no tiene un estado o Atlassian se cae, los comandos avisan en una línea y siguen. La única excepción es el gate de producción, que es justamente donde una persona tiene que decir que sí.
Calidad que no depende de acordarse
Las reglas que importan están en hooks y gates, no en la memoria de nadie. Al terminar cada turno del agente corre un quality gate con typecheck, lint y build. La primera versión corría el check completo de CI y bloqueaba constantemente por el árbol sucio de un trabajo a medias; lo reduje a lo que de verdad indica que algo está roto.
Ese gate es el único hook que bloquea. Todos los demás avisan y salen limpios. La regla está escrita en la guía de contribución: si un hook nuevo puede abortar el trabajo, el PR tiene que justificarlo. Un sistema que bloquea por todo termina desactivado.
- Verificación visual. Si un cambio toca estilos, el gate exige montar el componente aislado en el navegador y mirarlo. Un anillo de foco recortado compila perfecto, y ningún test de tipos lo atrapa.
- Una base desechable por corrida. Los tests de integración del backend corren contra un Postgres que se crea y se destruye en cada ejecución, con una guarda que rechaza cualquier base compartida.
- Pruebas por capa. Vitest para la lógica, Vitest con jsdom para componentes y pruebas end-to-end sobre Playwright para el camino completo, escritas como objetivos en lenguaje natural que se graban y se reproducen sin llamar al modelo hasta que la UI cambia.
- Review automático en cada PR. Claude revisa cada pull request desde GitHub Actions, el agente responde y resuelve cada hilo, y
/promoteno avanza con hilos abiertos.
Lo que no funcionó a la primera
Casi todo lo que describo arriba es la segunda o tercera versión. Estas son las correcciones que más me enseñaron.
Un subagente por criterio
La primera versión de /execute abría un subagente por cada criterio de aceptación. Sonaba ordenado, pero cada uno arrancaba en frío releyendo el spec, el plan y las convenciones del repo, y una feature de siete criterios pagaba siete veces ese arranque. Nada de eso compraba calidad. Ahora hay un worker por repo que recorre todos sus criterios en una sola sesión, sigue haciendo RED→GREEN uno a uno y se detiene en el primero que no cierra. Si el cambio es de un solo repo y tres archivos o menos, sin migración ni cambio de contrato, se hace sin subagente.
Un servidor MCP que no podía conectarse
Para hablar con Jira empecé con el servidor MCP oficial de Atlassian. Autenticado con un token de API, solo exponía herramientas que respondían todas You don't have permission to connect via API token, porque exigían OAuth. Con el mismo token, la API REST funcionaba sin problema. Lo retiré y escribí un cliente pequeño en bash: tres herramientas muertas ocupando contexto en cada sesión eran peor que no tener ninguna.
Una herramienta de review que dejó de compensar
La revisión de PRs la hacía Greptile, y el flujo ya sabía esperar sus comentarios y responder cada hilo. Cuando se quedó sin créditos, el plan de pago costaba entre 50 y 120 dólares al mes para nuestro volumen. Moví la review a la GitHub Action de Claude Code con las mismas reglas por stack, ahora en markdown, y el procedimiento de atender los hilos se quedó igual.
El contexto es un presupuesto
Cada skill que añadía al plugin era razonable por sí sola, y el catálogo crecía una skill por PR sin que nadie lo notara. Cuando lo medí, el plugin consumía unos 4.850 tokens en cada sesión antes de que el agente escribiera una línea: las descripciones del catálogo y el contexto del ecosistema que se inyecta al arrancar, y otra vez en cada /clear.
La poda no añadió ninguna capacidad, solo quitó peso. Los comandos que solo teclea una persona salieron del catálogo. El contexto del ecosistema se partió en un núcleo que viaja siempre y referencias que se leen cuando el trabajo las pide. Las skills largas se convirtieron en un enrutador que solo carga la parte que aplica.
El coste fijo bajó de unos 4.850 a unos 3.430 tokens, un 29% menos, y cada /clear o /compact se ahorra además los cerca de 730 tokens del contexto que se vuelve a inyectar. Lo que hace que el número no vuelva a subir es que ahora el CI lo mide: hay techo para las descripciones del catálogo, para lo que se inyecta por sesión y para la skill más grande. Si un PR no cabe, la salida no es subir el techo, sino recortar algo. Medirlo cambió la conversación de «añadamos una regla más» a «qué sacamos para que quepa».
Un andamiaje que caduca
Cada pieza del flujo existe porque asume algo que el modelo no hace bien solo. Esas suposiciones caducan: llega un modelo mejor y la pieza pasa de sostener peso a estorbar, cobrando latencia y tokens por resolver un problema que ya no existe. Si no queda escrito qué asume cada una, nadie se atreve a quitarlas y el andamiaje solo crece.
Por eso el repo tiene un documento que lista cada pieza con la limitación que asume, cómo comprobar si sigue haciendo falta y cuándo retirarla. Tres ejemplos:
| Pieza | Qué limitación asume | Cuándo retirarla |
|---|---|---|
| Quality gate al cerrar el turno | El agente da el trabajo por terminado sin correr typecheck, lint ni build. | Si con el gate desactivado una semana el CI no se rompe más. |
| Gate visual | El agente cambia una clase de CSS y da el cambio por bueno sin mirarlo. | Si el modelo empieza a abrir el navegador por su cuenta. |
| Diagnóstico con un loop que reproduce | Ante un bug, el modelo salta a una hipótesis leyendo código y arregla lo primero plausible. | Si sin la guía construye primero el repro y varias hipótesis. |
Con cada modelo nuevo, la tabla se recorre contra una feature real, no contra un ejemplo de juguete, porque un caso de juguete siempre dice que el andamiaje sobra. Lo que ya no aguanta peso se quita en un PR aparte para poder revertirlo. Y se mira también al otro lado: un modelo mejor no solo hace sobrar piezas, también hace viables otras que antes no lo eran.
El mismo documento lista lo que el flujo no hace, para que se note el hueco. El bucle entre el evaluador y la corrección está limitado a tres rondas: si tres vueltas no cierran un criterio, el problema suele estar en cómo está escrito, y eso lo arregla una persona. Y ninguna cantidad de andamiaje sustituye la aprobación real, que es humana y vive fuera del repo.
Operar en producción
El tercer plugin cubre lo que pasa después del merge. Los health checks comparan, por entorno, el commit desplegado con el de la rama y verifican que cada API diga en qué entorno está. Hay una guía para rastrear un error a través de los logs de la API, la base de datos y el frontend, y playbooks de incidente indexados por síntoma que solo cargan el que aplica.
El orden ante un incidente está fijado: primero estabilizar, con un rollback o un hotfix desde main, y después diagnosticar con calma. El diagnóstico sigue siempre el mismo método: un loop que reproduce el fallo antes de cualquier hipótesis, y un test de regresión que queda en el repo.
Lo que sigue
Hay una pregunta abierta que todavía no he decidido: si vale la pena una memoria que sobreviva al cierre de cada PR, para que el agente recuerde lo que aprendió en features anteriores. Antes de construirla quiero saber qué mediría, y la privacidad de los datos del entorno de pruebas podría responder la pregunta por sí sola.
Lo que me llevo de este trabajo es que el valor no está en que el agente escriba más código, sino en el sistema alrededor: un contrato que no se desalinea, responsabilidades separadas entre quien implementa y quien aprueba, y un andamiaje que se mide y se poda. Los modelos van a seguir mejorando. El trabajo es asegurarse de que el andamiaje no se convierta en su techo.
Notas
- Los datos de leads, clientes y PQRS están protegidos por la ley colombiana de protección de datos personales (Ley 1581 de 2012, conocida como Habeas Data). Ni los tests ni los agentes pueden tratarlos como datos de prueba. ↩

