# 0007b — Process manager: spawn, wait, kill, status

## Metadata

| Campo | Valor |
|-------|-------|
| **ID** | 0007b |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | feature |

## Dependencias

| ID | Título | Estado | Requerido |
|----|--------|--------|-----------|
| 0007a | Funciones core del DAG engine | pendiente | Si |

**Bloqueada por:** `#0007a`

**Desbloquea:** `#0007e`

---

## Objetivo

Funciones impuras para gestionar procesos hijo: lanzar, esperar, matar, consultar estado. Son los bloques que el executor usara para correr cada step de un DAG.

## Contexto

- Cada step de un DAG se ejecuta como un proceso hijo (`os/exec`)
- Necesitamos captura de stdout/stderr, timeout, señales (SIGTERM/SIGKILL)
- Deben ser funciones atomicas — el executor las compone
- Dominio `infra` porque gestionan recursos del sistema

## Arquitectura

```
functions/infra/
├── process_spawn.go      — NEW: lanza proceso, retorna PID + pipes
├── process_spawn.md
├── process_wait.go       — NEW: espera proceso con timeout
├── process_wait.md
├── process_kill.go       — NEW: envia señal a proceso (SIGTERM, SIGKILL)
├── process_kill.md
├── process_status.go     — NEW: consulta estado de PID (running, exited, code)
├── process_status.md

types/infra/
├── process_handle.md     — NEW: PID, stdin/stdout/stderr pipes, start_time
├── process_result.md     — NEW: exit_code, stdout, stderr, duration_ms
```

### Patron pure core / impure shell

- `core/` — No aplica en este issue
- `infra/` — Todas impuras (spawn procesos, I/O con OS)
- `error_type`: `error_go_core` para todas

## Tareas

### Fase 1: Tipos

- [ ] **1.1** Definir `ProcessHandle` — pid, cmd, start_time, working_dir
- [ ] **1.2** Definir `ProcessResult` — exit_code, stdout, stderr, duration_ms, killed

### Fase 2: Funciones

- [ ] **2.1** `process_spawn` — ejecuta comando con args, env, working_dir. Retorna ProcessHandle. No bloquea.
- [ ] **2.2** `process_wait` — espera a que el proceso termine o timeout. Retorna ProcessResult.
- [ ] **2.3** `process_kill` — envia SIGTERM, espera grace period, luego SIGKILL si sigue vivo
- [ ] **2.4** `process_status` — consulta si el PID sigue corriendo, retorna estado

### Fase 3: Tests

- [ ] **3.1** Tests: spawn+wait de `echo hello`, timeout con `sleep 999`, kill de proceso largo
- [ ] **3.2** Tests: captura correcta de stdout/stderr, exit codes no-zero

### Fase 4: Cleanup

- [ ] `fn index` y verificar IDs
- [ ] Verificar error_type en todas las funciones impuras

---

## Ejemplo de uso

```go
handle, err := process_spawn(ProcessSpawnInput{
    Command:    "python3",
    Args:       []string{"script.py", "--flag"},
    Env:        []string{"API_KEY=xxx"},
    WorkingDir: "/home/lucas/project",
})

result, err := process_wait(handle, 30*time.Second) // timeout 30s
// result.ExitCode == 0, result.Stdout == "output..."

// O matar si tarda demasiado
process_kill(handle, 5*time.Second) // SIGTERM, 5s grace, luego SIGKILL
```

## Decisiones de diseno

- **Spawn no bloquea**: retorna handle inmediatamente, wait es separado — permite al executor lanzar steps en paralelo
- **Kill con grace period**: SIGTERM primero, espera, SIGKILL si no murio — comportamiento estandar de process managers
- **Stdout/stderr como strings**: para steps cortos. Para steps con output grande, futuro: streaming a archivo

## Criterios de aceptacion

- [ ] Spawn y wait funcionan con comandos reales
- [ ] Timeout mata el proceso correctamente
- [ ] Kill con grace period funciona
- [ ] Exit codes se capturan correctamente
- [ ] Tests pasan
- [ ] Indexado en registry.db
