Por Thales y Claude -- CEO y CTO de IA, ZeroSuite, Inc.
El 14 de febrero de 2026, nos sentamos a construir el segundo producto del ecosistema ZeroSuite. Al final de la sesion, teniamos un servidor API Rust completamente estructurado con 14 endpoints, 8 tablas de base de datos, un motor de programacion, un ejecutor HTTP con logica de reintento, un parser de lenguaje natural, un sistema de notificaciones, una capa de encriptacion -- y un sitio web de marketing con 15 secciones, soporte bilingue y un toggle de tema claro/oscuro.
41 archivos. 3,671 lineas de codigo. Cero errores de compilacion.
El metodo: cuatro agentes de IA trabajando en paralelo, coordinados por un lider de equipo, con ordenamiento explicito de dependencias para que nada se bloqueara innecesariamente.
Asi es como funciono.
La estructura del equipo
Claude Code soporta un primitivo TeamCreate que genera multiples agentes en worktrees aislados. Cada agente obtiene su propia copia del repositorio, trabaja independientemente, y fusiona sus cambios de vuelta. La clave esta en el grafo de dependencias -- que agentes pueden comenzar inmediatamente, y cuales deben esperar a que otro termine.
Aqui esta el equipo que ensamblamos:
team-lead (coordinador)
|
+-- website-builder -> Tarea #1: Sitio web de marketing (independiente)
+-- api-architect -> Tarea #2: Esqueleto del proyecto Rust (independiente)
+-- core-engine -> Tarea #3: Servicios de logica de negocio (bloqueado por #2)
+-- api-routes -> Tarea #4: Manejadores HTTP + rutas (bloqueado por #2)Cuatro agentes, dos oleadas de ejecucion:
Oleada 1 (paralela): website-builder y api-architect comienzan simultaneamente. El sitio de marketing no tiene dependencia del codebase de la API, y el esqueleto de la API no tiene dependencia del sitio web. Pueden trabajar en completo aislamiento.
Oleada 2 (paralela, despues de la Oleada 1): Una vez que api-architect termina la estructura del proyecto -- Cargo.toml, layout de directorios, definiciones de modelos, esquema de base de datos, tipos de error -- los agentes core-engine y api-routes se desbloquean. core-engine escribe la capa de servicios (programador, ejecutor, parser NLP, notificaciones, secretos). api-routes escribe los manejadores HTTP, tipos de request/response, middleware y ensamblaje del router. Ambos construyen sobre el esqueleto pero trabajan en directorios diferentes, asi que proceden en paralelo.
El website-builder se ejecuta independientemente durante todo el proceso. De hecho fue el ultimo en terminar porque produjo el archivo individual mas grande -- 597 lineas de HTML con CSS y JavaScript inline.
Oleada 1: El esqueleto de la API
El trabajo del agente api-architect era crear la base sobre la que los otros dos agentes construirian. Esta es la tarea mas critica en la cadena de dependencias -- si los tipos estan equivocados, o la estructura de directorios es incomoda, o el patron de manejo de errores es inconsistente, todo lo posterior hereda esos problemas.
El agente produjo la siguiente estructura:
0cron-core/
+-- Cargo.toml 28 dependencias
+-- Dockerfile constructor Rust multi-stage -> debian-slim
+-- .env.example 6 variables de entorno
+-- src/
+-- main.rs Punto de entrada del servidor
+-- config.rs AppConfig desde entorno
+-- error.rs Enum AppError + IntoResponse
+-- models/
| +-- mod.rs Re-exportaciones
| +-- user.rs Struct User
| +-- team.rs Team + TeamMember
| +-- job.rs Job + JobConfig + RetryConfig
| +-- execution.rs Struct Execution
| +-- secret.rs Struct Secret
| +-- monitor.rs Struct Monitor
| +-- api_key.rs Struct ApiKey
+-- db/
| +-- mod.rs Struct Database + connect + migrate
| +-- migrations/
| +-- 001_initial.sql Esquema completo (8 tablas + indices)
+-- services/
| +-- mod.rs Declaraciones de modulos (stubs)
+-- api/
+-- mod.rs Esqueleto del router + AppState
+-- types.rs Placeholder
+-- middleware/
+-- handlers/El entregable critico fue la estructura AppState y el esquema de base de datos. Todo lo demas en el sistema fluye a traves de estos dos artefactos.
El AppState es deliberadamente minimo:
rust#[derive(Debug, Clone)]
pub struct AppState {
pub db: Database,
pub redis: redis::Client,
pub config: Arc<AppConfig>,
}Tres campos. Database envuelve un PgPool de SQLx. redis::Client es la fabrica de conexiones para Redis. AppConfig contiene configuraciones derivadas del entorno envueltas en Arc para clonacion economica entre tareas async.
Esta estructura se pasa como estado de Axum a cada manejador. Sin framework de inyeccion de dependencias, sin trait objects, sin patron service locator. El pool de base de datos maneja la multiplexion de conexiones internamente. El cliente Redis crea conexiones bajo demanda. La configuracion es inmutable despues del arranque.
El esquema de 8 tablas mapea directamente al dominio:
| Tabla | Proposito | Decision clave de diseno |
|---|---|---|
users | Cuentas con email, hash de contrasena, OAuth, zona horaria, plan | ID de cliente Stripe almacenado directamente para integracion de facturacion |
teams | Contenedores organizacionales | Cada usuario obtiene un equipo predeterminado al registrarse |
team_members | Membresia basada en roles (admin, editor, viewer) | Clave primaria compuesta en (team_id, user_id) |
jobs | Definiciones de tareas cron | config como JSONB para flexibilidad; estadisticas de runtime desnormalizadas |
executions | Historial completo de ejecuciones | Request y response capturados; eliminacion en cascada con la tarea |
secrets | Valores encriptados con AES-256-GCM | Alcance por equipo, unico en (team_id, key) |
monitors | Monitores heartbeat/ping | Token de ping unico para verificaciones de salud entrantes |
api_keys | Tokens de autenticacion | Hash almacenado, prefijo indexado para busqueda |
Oleada 2: El motor central
Una vez que el esqueleto estuvo en su lugar, core-engine recibio los modelos, el esquema de base de datos y los tipos de error como contexto. Su trabajo: implementar los siete modulos de servicio que contienen toda la logica de negocio.
Aqui es donde viven las verdaderas decisiones de ingenieria. Los servicios son donde la teoria de programacion se encuentra con la realidad de produccion.
El archivo mas importante es scheduler.rs -- 198 lineas que forman el corazon latiente del producto entero:
rustpub struct Scheduler {
redis: redis::Client,
pool: PgPool,
config: Arc<AppConfig>,
}
impl Scheduler {
pub fn start(self: Arc<Self>) -> tokio::task::JoinHandle<()> {
tokio::spawn(async move {
tracing::info!("Scheduler started");
loop {
if let Err(e) = self.poll().await {
tracing::error!("Scheduler poll error: {e}");
}
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
}
})
}
}Una tarea de fondo tokio que hace polling cada segundo. Dentro de poll(), consulta un sorted set de Redis para tareas cuya puntuacion (marca de tiempo de proxima ejecucion) es menor o igual a now, adquiere un lock distribuido para prevenir doble ejecucion, genera una tarea de ejecucion, y reprograma la tarea para su siguiente ejecucion.
El inventario completo de servicios de este agente:
| Servicio | Lineas | Responsabilidad |
|---|---|---|
scheduler.rs | 198 | Polling de sorted set Redis, locks distribuidos, calculo de proxima ejecucion |
executor.rs | 228 | Ejecucion HTTP, interpolacion de secretos, logica de reintento, logging de resultados |
nlp_parser.rs | 151 | Traduccion de lenguaje natural a expresion cron (~20 patrones) |
notifications.rs | 229 | Slack Block Kit, embeds de Discord, Telegram Bot API, webhook generico, email |
secrets.rs | 92 | Encriptacion/desencriptacion AES-256-GCM, interpolacion ${secrets.KEY} |
monitors.rs | 104 | CRUD de heartbeat, generacion de token de ping, deteccion de pings perdidos |
jobs.rs | 272 | CRUD completo, resolucion de horarios, pausar/reanudar, trigger manual, estadisticas |
Total de core-engine: 1,274 lineas de logica de negocio.
La fusion: 0 errores, 53 advertencias
Cuando los cuatro agentes completaron, el lider de equipo fusiono sus cambios. Este es el momento de la verdad en cualquier construccion en paralelo -- las piezas realmente encajan?
cargo checkResultado: 0 errores. 53 advertencias.
Cada advertencia era la misma: "unused import" o "unused variable." Esto es esperado y correcto. El agente core-engine escribio funciones de servicio que los manejadores del agente api-routes llaman -- pero como los agentes trabajaron en paralelo, ninguno podia verificar los sitios de llamada durante el desarrollo.
Cero errores significa que los contratos de tipo fueron correctos. La estructura AppState coincidio entre api/mod.rs y main.rs. Los tipos de modelo en services/ coincidieron con lo que handlers/ esperaba. Los tipos de retorno de consultas de base de datos coincidieron con las estructuras de modelo. El tipo de error implemento IntoResponse correctamente.
Esto no es accidental. Es consecuencia de tres cosas:
- El esqueleto fue bien disenado. El agente
api-architectdefinio los tipos compartidos, el patron de error y la estructura de estado antes de que nadie mas empezara a escribir codigo.
- El sistema de tipos de Rust detecta errores de integracion en tiempo de compilacion. Si
core-enginedefinio una firma de funcion queapi-routesllamo incorrectamente,cargo checklo habria detectado.
- Los agentes trabajaron en directorios claramente delimitados.
core-engineposeiaservices/.api-routesposeiaapi/handlers/,api/types.rsyapi/middleware/. No hubo conflictos a nivel de archivo porque el trabajo fue particionado por directorio, no por funcionalidad.
El recuento final
Esto es lo que la sesion produjo:
| Entregable | Archivos | Lineas | Agente |
|---|---|---|---|
| Sitio web de marketing | 4 | 597 (HTML) | website-builder |
| Esqueleto del proyecto Rust | 14 | ~400 | api-architect |
| Servicios de logica de negocio | 8 | 1,274 | core-engine |
| Manejadores HTTP + rutas | 7 | ~900 | api-routes |
| Compartido (modelos, config, error, DB) | 12 | ~500 | api-architect |
| Total | 41 | ~3,671 | 4 agentes |
Lo que la sesion no produjo:
- Tests (diferidos deliberadamente -- preferimos estabilizar la superficie de la API antes de escribir tests de integracion)
- Dashboard frontend (sesion separada, agentes separados)
- Configuracion de despliegue (manejada en una sesion posterior con Docker Compose)
- Integracion de facturacion (anadida en una sesion de seguimiento el 11 de marzo)
Por que este enfoque funciona
La construccion en paralelo de cuatro agentes no es el enfoque correcto para todo. Funciona especificamente cuando:
- Los entregables son claramente separables. Un sitio web de marketing y una API Rust tienen cero superposicion de codigo.
- El grafo de dependencias es poco profundo. Dos oleadas, no cinco. Si el agente C depende del agente B que depende del agente A, tienes un pipeline serial disfrazado de sistema paralelo.
- Los contratos de interfaz se establecen por adelantado. El agente de esqueleto definio los tipos antes de que los agentes de implementacion empezaran.
- El lenguaje tiene un sistema de tipos fuerte. El compilador de Rust es, en efecto, un quinto agente -- un tester de integracion que se ejecuta despues de la fusion y detecta cada incompatibilidad estructural.
El paralelismo no se trata solo de velocidad. Se trata de mantener a cada agente profundo en un solo dominio.
Esta es la Parte 2 de una serie de diez partes sobre la construccion de 0cron.dev. Siguiente: Construyendo un motor de programacion cron en Rust -- una inmersion profunda en sorted sets de Redis, bloqueo distribuido y la arquitectura de polling basada en ticks que hace funcionar el programador de 0cron (literalmente).