---
name: orquestacion-async
description: >
  Patrones de orquestación asíncrona para agentes: subagentes fire-and-forget,
  gestión de tareas paralelas, corrección mid-task y supervisión no-bloqueante.
  Basado en Deep Agents v0.5 (LangChain). Cargar cuando orquestador-swl necesite
  ejecutar implementaciones en paralelo o gestionar agentes de larga duración.
version: "1.1.0"
evolved: true
evolved-from: "1.0.0"
evolved-at: "2026-04-24"
evolved-by: "aprender"
evolved-note: "Sección nueva: matriz agente × archivo con intersección vacía antes de lanzar paralelos"
herramientasPermitidas: [Read]
user-invocable: false
exclusiones:
  - "No cargar para concurrencia Python pura con asyncio/aiohttp — si la tarea es I/O concurrente en código de aplicación (no orquestación de agentes SWL), cargar `async-python`."
  - "No cargar para lanzar un solo agente secuencialmente — el overhead de las 5 herramientas de gestión solo vale si hay ≥2 agentes en paralelo o una tarea de larga duración que bloquea el supervisor."
  - "No cargar para delegar un slice a un subagente sin necesidad de supervisión ni corrección mid-task — para delegación simple sin monitoreo, el orquestador puede invocar el agente directamente sin este patrón."
  - "No cargar para pipelines de datos o ETL con pasos dependientes — los patrones fire-and-forget asumen independencia entre tareas; para pipelines ordenados con dependencias usar `datos-etl`."
evolvable: true  # default para skill estandar
---
# Orquestación Asíncrona de Agentes

Patrones para supervisión no-bloqueante de subagentes, basados en Deep Agents v0.5
y adaptados al sistema de delegación de swl-ses.

## Cuándo NO cargar

- La tarea es concurrencia Python con asyncio/aiohttp (no agentes SWL) — cargar `async-python`.
- Se lanza un solo agente secuencialmente sin necesidad de supervisión — la delegación directa es más simple.
- El pipeline tiene pasos con dependencias ordenadas — para ETL con dependencias cargar `datos-etl`.

## Cuándo cargar

- `orquestador-swl` necesita lanzar múltiples implementaciones en paralelo
- Una tarea toma más de 3 minutos y bloquea la interacción con el usuario
- Se necesita corrección mid-task de un agente en ejecución
- Gestión de múltiples agentes concurrentes con diferentes prioridades

## Principio fundamental

> Un supervisor NUNCA debe bloquearse esperando a un subagente de larga duración.
> El supervisor debe poder: responder al usuario, lanzar más tareas, verificar
> progreso, y corregir el rumbo — todo mientras los subagentes trabajan.

## Patrón: 5 herramientas de gestión async

Inspirado en el modelo de Deep Agents v0.5:

### 1. `start_async_task` — Lanzar tarea
```
Entrada: { agente, prompt, prioridad, timeout }
Salida:  { taskId, estado: "en_progreso" }
```
El supervisor recibe un ID de tarea inmediatamente y continúa operando.

### 2. `check_async_task` — Verificar estado
```
Entrada: { taskId }
Salida:  { estado, progreso, resultado_parcial, errores }
```
Polling ligero — no bloquea, retorna estado actual.

### 3. `update_async_task` — Corrección mid-task
```
Entrada: { taskId, instrucciones_adicionales }
Salida:  { aceptado: true/false }
```
Envía instrucciones de corrección al subagente en ejecución.
Solo funciona si el subagente está en estado "en_progreso".

### 4. `cancel_async_task` — Cancelar tarea
```
Entrada: { taskId, razon }
Salida:  { cancelado: true, trabajo_parcial }
```
Cancela un subagente y recupera trabajo parcial.

### 5. `list_async_tasks` — Listar tareas
```
Entrada: { filtro_estado? }
Salida:  [{ taskId, agente, estado, inicio, duracion }]
```
Vista general de todas las tareas gestionadas.

## Estados de una tarea async

```
                    ┌─────────────┐
                    │   creada    │
                    └──────┬──────┘
                           │ start
                    ┌──────▼──────┐
            ┌───── │ en_progreso  │ ─────┐
            │      └──────┬──────┘      │
      update│             │             │ cancel
            │      ┌──────▼──────┐      │
            └────► │   exitosa   │      │
                   └─────────────┘      │
                   ┌──────▼──────┐      │
                   │   fallida   │ ◄────┘
                   └─────────────┘
                   ┌─────────────┐
                   │  cancelada  │
                   └─────────────┘
```

## Implementación en swl-ses

### Mapeo a herramientas existentes

| Herramienta async | Implementación swl-ses |
|-------------------|----------------------|
| `start_async_task` | `Agent()` con `run_in_background: true` |
| `check_async_task` | Verificar notificación de agente completado |
| `update_async_task` | `SendMessage` al agente con `to: agentId` |
| `cancel_async_task` | No disponible nativamente — requiere convención |
| `list_async_tasks` | `task-service.js` (hooks/lib/) |

### Patrón de uso en orquestador-swl

```markdown
## Fase de implementación paralela

1. Analizar el plan e identificar slices independientes
2. Para cada slice independiente:
   - Lanzar Agent() con run_in_background: true
   - Registrar taskId en task-service.js
3. Mientras los agentes trabajan:
   - Responder al usuario sobre progreso
   - Revisar resultados parciales si llegan notificaciones
   - Lanzar nuevos agentes si hay más slices listos
4. Cuando todos completen:
   - Verificar resultados con revisor-codigo-swl
   - Resolver conflictos si dos agentes tocaron los mismos archivos
```

### Corrección mid-task

Cuando un agente en background necesita ajuste:

```markdown
1. Detectar desviación (por notificación o verificación manual)
2. Enviar SendMessage al agentId con instrucciones de corrección:
   - "El endpoint cambió de /api/v1/users a /api/v2/users"
   - "Usar SQLAlchemy async, no sync"
3. El agente recibe la corrección en su contexto y ajusta
```

### Resolución de conflictos

Cuando dos agentes modifican el mismo archivo:

```
1. Detectar conflicto: edit-accumulator.js registra archivos tocados
2. Si hay solapamiento:
   a. Revisar diffs de ambos agentes
   b. Determinar si son complementarios o contradictorios
   c. Complementarios → merge manual o automático
   d. Contradictorios → escalar al usuario con ambas versiones
3. Nunca aplicar ciegamente el último cambio
```

## Límites y restricciones

### De swl-ses
- `MAX_DEPTH = 2` — Un agente puede delegar, pero el delegado no puede delegar
- `max_concurrent_children = 3` — Máximo 3 subagentes concurrentes (configurable)
- Agentes delegados NO escriben a memoria compartida (MEMORY.md)
- Agentes delegados NO interactúan con el usuario directamente

### Del runtime (Claude Code)
- Background agents notifican al completar — no hay polling periódico
- El contexto del agente padre no ve los tool calls internos del hijo
- SendMessage requiere el agentId exacto retornado por Agent()

## Escenarios de uso

### Escenario A: Feature con frontend + backend + tests
```
orquestador-swl:
  1. Lanza backend-api-swl (Agent, background) → taskId_1
  2. Lanza frontend-react-swl (Agent, background) → taskId_2
  3. Espera notificaciones
  4. Backend completa → lanza tdd-qa-swl para tests de API → taskId_3
  5. Frontend completa → lanza tdd-qa-swl para tests de UI → taskId_4
  6. Todos completan → revisor-codigo-swl verifica todo junto
```

### Escenario B: Refactoring paralelo
```
orquestador-swl:
  1. Divide módulos a refactorizar en grupos independientes
  2. Lanza N agentes en paralelo (máx 3)
  3. Cuando completa un grupo → verifica y lanza el siguiente
  4. Al final → merge + test completo
```

### Escenario C: Investigación + implementación
```
orquestador-swl:
  1. Lanza investigador-swl (background) para research
  2. Mientras, prepara scaffold básico directamente
  3. Investigador completa → lee resultados
  4. Lanza implementador-swl con contexto del research
```

## Métricas de eficiencia

| Métrica | Fórmula | Objetivo |
|---------|---------|----------|
| Paralelismo efectivo | Tiempo secuencial / Tiempo real | ≥ 2.0x |
| Conflictos por tarea | Archivos con conflicto / Total archivos | ≤ 5% |
| Reintentos por corrección | Correcciones mid-task / Total tareas | ≤ 20% |
| Overhead de orquestación | Tokens orquestador / Tokens total | ≤ 15% |

## Gotchas / Errores comunes no obvios

- **`check_task_status` retorna `completada` pero el archivo generado por el subagente no existe en disco**: el subagente reportó éxito pero terminó antes de escribir el archivo por una excepción silenciada. Causa: el estado `completada` en el patrón de 5 herramientas refleja que el subagente terminó su ejecución sin lanzar excepción al supervisor — si el error fue interno y capturado silenciosamente, el output no existe aunque el estado sea correcto. Solución: la herramienta `get_task_result` debe verificar explícitamente que los artefactos de output existen antes de reportar `completada`; agregar validación de postcondición: `assert Path(archivo_esperado).exists()`.
- **Lanzar 8 subagentes en paralelo con `start_async_task` sin semáforo consume el presupuesto de tokens del orquestador en segundos**: cada invocación de subagente consume el contexto del orquestador mientras espera resultados de todos ellos. Causa: el patrón fire-and-forget no limita la concurrencia — 8 agentes en paralelo pueden disparar 8× el costo esperado si el supervisor los monitorea activamente. Solución: limitar con un semáforo de concurrencia máxima de 3-4 agentes simultáneos, y usar `check_task_status` con backoff para no encuestar continuamente.
- **Corrección mid-task con `modify_task_prompt` produce un subagente que ignora la corrección y continúa con el plan original**: el subagente ya tiene el plan congelado en su contexto y la corrección llega como un mensaje adicional que no modifica el plan original. Causa: `modify_task_prompt` agrega instrucciones al contexto del agente pero si el plan ya está en ejecución, el subagente puede priorizarlo sobre las instrucciones nuevas. Solución: la corrección mid-task debe ser explícita sobre qué cambiar: en lugar de agregar "también haz X", indicar "STOP el paso actual, cambia a Y" con autoridad clara sobre el paso en conflicto.
- **`cancel_task` no libera los archivos que el subagente dejó en estado parcial**: cancelar una tarea detiene el agente pero los archivos que ya escribió permanecen con contenido incompleto. Causa: el patrón de cancelación no tiene rollback de los artefactos ya generados — solo detiene la ejecución futura. Solución: antes de `cancel_task`, registrar los archivos que el subagente está modificando (via `check_task_status` que devuelve `archivos_en_progreso`), y después de la cancelación, revertir esos archivos con `git checkout -- archivos` si el trabajo no es recuperable.

---

## Matriz agente × archivo: partición explícita antes de lanzar paralelos

### Regla

Antes de lanzar **N agentes en paralelo** que modifican archivos, producir una
**matriz explícita** agente × archivo con **intersección vacía** entre filas.
Un archivo compartido por 2 agentes paralelos produce merge silencioso
impredecible: el último write gana, sin detección ni log.

### Por qué

Los agentes fire-and-forget no tienen coordinación entre sí. Si el agente A
está editando `core/service.py` y el agente B también, la secuencia real es:

1. A lee versión V0, piensa, escribe V1.
2. B lee versión V0 (no V1), piensa, escribe V2.
3. Resultado: los cambios de A desaparecen. Git no muestra conflicto porque
   B ya "reconcilió" en su cabeza el archivo.

El patrón no se detecta por tests unitarios aislados. Solo se observa al leer
el diff final contra lo esperado — y suele descubrirse horas después.

### Patrón canónico: matriz explícita pre-lanzamiento

```markdown
## Matriz de partición — Sprint X

| Agente | Archivos (exclusivos) |
|--------|----------------------|
| backend-python-swl | api/routers/emaia.py, core/service_usuarios.py |
| frontend-react-swl | ui/pages/usuarios.tsx, ui/components/FormUsuario.tsx |
| datos-swl | core/db/models.py, alembic/versions/00XX_usuarios.py |
| tdd-qa-swl | tests/integration/test_usuarios.py (solo lectura en el resto) |

Verificación: intersección de archivos entre filas = ∅.
Archivos no listados: NO se tocan en este sprint.
```

### Protocolo operativo

1. **Antes de invocar agentes en paralelo**, el orquestador escribe la matriz
   en `.planning/sprint-X/matriz-agentes.md` o equivalente.
2. **Cada agente delegado recibe explícitamente** su lista de archivos en
   el prompt de delegación: `"Tus archivos asignados son X, Y, Z. No tocar
   ningún otro archivo sin escalar al orquestador."`.
3. **Si un agente necesita tocar un archivo no asignado**, debe escalar
   (pausar y reportar), no tomar decisión unilateral.
4. **Post-ejecución**: verificar que el diff de cada agente está contenido
   en sus archivos asignados. Un agente que tocó un archivo ajeno es señal
   de drift del scope y debe revertirse.

### Casos donde NO aplica

- **Tareas de un solo agente** (serial): la matriz es innecesaria.
- **Lecturas sin writes**: múltiples agentes pueden leer el mismo archivo
  en paralelo sin riesgo; la matriz aplica a writes.
- **Archivos append-only con locking** (JSONL con `fs.appendFileSync`):
  los appends atómicos no se pisan. Documentar explícitamente estos casos.

### Anti-patrón

```markdown
# MAL — delegación sin partición
"Lanzar backend-python-swl, frontend-react-swl y datos-swl en paralelo
 para implementar la feature X."

# Cada agente descubre por sí mismo qué archivos toca. Garantizado:
# los 3 tocan `core/schemas.py` porque todos necesitan el tipo Usuario.
```

### Relacionado

- `checkpoints-verificacion` — cómo pausar agentes en checkpoints HITL.
- `git-worktrees-paralelo` — aislamiento físico con worktrees cuando la
  matriz no se puede diseñar con intersección vacía.
- Regla en CLAUDE.md: "Matriz agente × archivo antes de lanzar paralelos
  N≥3" — consolidar como regla del proyecto si el patrón se repite.
