--- id: "0007b" title: "Process manager: spawn, wait, kill, status" status: completado type: feature domain: [] scope: multi-app priority: alta depends: [] blocks: [] related: [] created: 2026-05-17 updated: 2026-05-17 tags: [] --- # 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