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.

ROL
CTO · Software Engineer
PERIODO
2025—hoy

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.

CRMReact · ViteApp de técnicosReact 19 · ViteFormularioReact 19 · SanitySitio webNext.js 16HTTPAPIExpress 5 · Prisma 7 · Zodcontrato →tipos de cada clientePrismaPostgresSupabase · RLS · storage
Los cuatro clientes consumen la API por HTTP y sincronizan sus tipos desde el contrato del backend. Solo la API habla con la base de datos.

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.

  1. 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
  2. 02 CONSTRUIR
    • /executeTDD: backend primero, luego cada frontend
    • /fixDiagnóstico hasta dejar todo en verde
  3. 03 VERIFICAR
    • /reviewEvaluador independiente, criterio por criterio
    • /securityAutorización, PII, secretos y RLS
  4. 04 ENTREGAR
    • /prSolo si todos los criterios están verificados
    • /promotePruebas → aprobación → producción
Las cuatro fases del flujo y los comandos que las mueven. Un cambio no pasa a la siguiente fase hasta que la anterior deja su evidencia en el repo.

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 /promote no 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.

Coste fijo por sesión: de 4.850 a 3.430 tokens. Inyectado al arrancar: de 6.326 a 3.410 bytes. Catálogo de skills: de 12.236 a 9.514 caracteresAntesDespuésCoste fijo por sesióntokensAntes: 4.850 tokens4.850Después: 3.430 tokens (−29%)3.430 −29%Inyectado al arrancarbytesAntes: 6.326 bytes6.326Después: 3.410 bytes (−46%)3.410 −46%Catálogo de skillscaracteresAntes: 12.236 caracteres12.236Después: 9.514 caracteres (−22%)9.514 −22%
Coste fijo del plugin por sesión antes y después de la poda, medido con el validador del repo. Cada barra está indexada a su valor inicial.

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:

PiezaQué limitación asumeCuándo retirarla
Quality gate al cerrar el turnoEl 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 visualEl 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 reproduceAnte 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

  1. 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. ↩
  • Claude Code
  • Agentes de IA
  • Arquitectura de software
  • TDD
  • CI/CD
  • Node.js
  • Prisma
  • Supabase

MÁS ARTÍCULOS