--- name: agent-orchestrator description: >- Turns this Claude Code session (VS Code extension) into an orchestrator that launches autonomous agents in tmux, each in its own git worktree/branch, monitors them, talks to them, documents them in Docmost, and opens PRs via the aleleba-pr skill — NEVER merging. ACTIVATE ONLY when the user explicitly says one of: "quiero dejar esto trabajando en background", "ejecuta esto solo", "lanza un agente para esto", "deja esto corriendo en background", "trabaja esto en background" (or a very close variant). Also handles monitoring ("¿qué está haciendo AGENT_NAME?", "revisa el estado de los agentes") and validation/archival ("apruebo AGENT_NAME", "/aprobar AGENT_NAME"). Do NOT activate on any other phrasing. effort: high --- # agent-orchestrator — Orquestación de agentes en background Convierte la sesión madre en un orquestador de agentes autónomos. Cada agente corre en su propia **sesión tmux interactiva**, en su propio **git worktree** sobre una **rama dedicada**, se **documenta en Docmost** y abre su **PR con la skill `aleleba-pr`**. **Los agentes nunca hacen merge**; el merge solo lo hace la conversación principal y con validación explícita del usuario. --- ## TRIGGER — Cuándo activarse (ESTRICTO) Esta skill **solo** se activa para **lanzar** un agente cuando el usuario dice explícitamente una de estas frases (o variante muy cercana): - "quiero dejar esto trabajando en background" - "ejecuta esto solo" - "lanza un agente para esto" - "deja esto corriendo en background" - "trabaja esto en background" Si el usuario no usa estas frases, **NO se activa** bajo ninguna circunstancia. > **Regla de desempate ante duda**: si no estás seguro de si la frase del usuario cuenta como "variante muy > cercana" a las de la lista, trátala como que **NO** activa la skill — pregúntale directamente al usuario > si quiere que lances un agente en background, en vez de asumirlo. Es preferible preguntar una vez de más > que activar el modo background sin que el usuario lo haya pedido. Una vez existan agentes, la skill **también** atiende: - **Monitoreo**: "¿qué está haciendo AGENT_NAME?", "revisa el estado de los agentes". - **Desbloqueo**: "responde a AGENT_NAME que ...", "dile a AGENT_NAME ...". - **Validación/archivo**: "apruebo AGENT_NAME", "valido el trabajo de AGENT_NAME", `/aprobar AGENT_NAME`. --- ## REGLAS DE SEGURIDAD (madre y agente) Reglas que la madre y todos los agentes deben seguir en todas las fases. ### Política de MERGE - **Los AGENTES NUNCA mergean** (ni `git merge`/`git rebase` hacia `main`/`master`/`dev`, ni vía MCP de Gitea/GitHub). El trabajo del agente **termina en el PR**. - **La conversación PRINCIPAL (madre) SÍ puede mergear**, pero **SOLO con validación explícita del usuario para ese merge concreto** (cada vez). Sin un "sí, mergea" explícito del usuario, no se mergea. Recuerda que en este entorno mergear a `master` **dispara el deploy a producción** (GitOps) — confírmalo con el usuario antes. ### NUNCA atribuir a Claude — REGLA ABSOLUTA **NUNCA agregues `Co-Authored-By: Claude` en NINGÚN commit.** Esto es absolutamente prohibido. No agregues `Co-Authored-By: Claude`, "Generated with Claude Code", "🤖", "creado con Claude" ni nada similar en **commits, PRs, comentarios de código, mensajes, ni Docmost**. Esta regla aplica tanto a la madre como a los agentes (y a sus subagentes). Los mensajes deben ir sin firma de Claude. **En cada commit, verifica que el mensaje NO contenga ninguna mención a Claude, Co-Authored-By, ni emojis de robots.** ### Aislamiento - La sesión madre **nunca** toca el árbol de trabajo del agente: cada agente vive aislado en su worktree. - No exponer el `GMAIL_APP_PASSWORD` en logs ni en Docmost. --- ## FASE 1: LANZAR ### 1. Identificar el proyecto → `PROJECT_NAME` En este orden de prioridad: ```bash PROJECT_NAME=$(jq -r '.name // empty' package.json 2>/dev/null) [ -z "$PROJECT_NAME" ] && PROJECT_NAME=$(grep -oP '^\s*name\s*=\s*"\K[^"]+' pyproject.toml 2>/dev/null | head -1) [ -z "$PROJECT_NAME" ] && PROJECT_NAME=$(basename "$(git rev-parse --show-toplevel 2>/dev/null || pwd)") echo "PROJECT_NAME=$PROJECT_NAME" ``` ### 2. Pedir la tarea Si el usuario no describió la tarea concretamente, **pídesela** antes de continuar. ### 3. Generar `AGENT_NAME` Slug descriptivo basado en la tarea: minúsculas, guiones, sin espacios ni caracteres especiales, prefijo `agente-`. Ejemplos: `agente-auth-endpoint`, `agente-fix-login-bug`, `agente-refactor-payments`. ### 4. Preparar `.worktrees` y crear el worktree > **IMPORTANTE: El worktree SIEMPRE se crea dentro de la carpeta `.worktrees/` del proyecto actual.** > Esta carpeta se llama `.worktrees/` y está en la raíz del proyecto en el que estás trabajando en ese momento. > No uses ningún otro nombre ni ruta para los worktrees. ```bash mkdir -p .worktrees grep -qxF '.worktrees/' .gitignore 2>/dev/null || echo '.worktrees/' >> .gitignore if [ -f .dockerignore ]; then grep -qxF '.worktrees/' .dockerignore || echo '.worktrees/' >> .dockerignore; fi git worktree add ".worktrees/$AGENT_NAME" -b "$AGENT_NAME" ``` ### 5. Crear en Docmost la subpágina del agente (ANTES de lanzar tmux) > **Este paso va ANTES de lanzar tmux**, para que `TASK.md` pueda incluir el `pageId`/`spaceId` reales desde > el primer momento — el agente no debe adivinarlos ni buscarlos. En el space del proyecto `PROJECT_NAME` (localizar con `mcp__docmost__list_spaces` / `mcp__docmost__search`). > **Regla concreta para encontrar el space** (el mismo criterio que usa la skill `docmost-context`): compara > `PROJECT_NAME` contra el `name` y el `slug` de cada space **normalizando** ambos lados — minúsculas, sin > espacios/guiones/guiones bajos, y sin el scope de npm si lo tiene (`@algo/nombre` → `nombre`). Si no hay > match exacto tras normalizar, prueba un match por contención (uno contiene al otro). Si tras eso **sigue > sin haber un match claro** (cero coincidencias, o varias ambiguas), **NO adivines ni "uses el más > adecuado"**: usa `AskUserQuestion` mostrando la lista de spaces disponibles (nombre + descripción) y deja > que el usuario elija uno, o cree uno nuevo si ninguno corresponde. > **Formato de tablas en Docmost:** las tablas markdown deben enviarse en formato de varias líneas (una línea > por fila), NO colapsadas en una sola línea, para que Docmost las renderice correctamente. Usa tablas > **siempre que se pueda** — tanto para "Agentes Activos" como para la subpágina de cada agente. a) Página **"Agentes Activos"** — si no existe, créala (`create_page`) con DOS tablas/secciones: - **"Agentes Activos"**: columnas Nombre, Session ID, Tarea, Estado, Inicio, Worktree, Rama. - **"Historial"**: columnas Nombre, Fecha de cierre, Resumen breve — vacía (solo encabezado) al crearla. Es un **registro permanente**: se le van agregando filas al archivar cada agente (FASE 4), pero **NUNCA se borra ni se sobrescribe una fila ya existente en ella**, bajo ninguna circunstancia. Agrega la fila del nuevo agente en la tabla "Agentes Activos" con Session ID = "pendiente" (aún no existe la sesión tmux). Para añadir la fila, lee la página completa primero y reescríbela con `update_page`, **preservando íntegra la sección "Historial"** tal cual estaba (tabla → no se rompe). b) **Subpágina** bajo "Agentes Activos" llamada `AGENT_NAME` (`create_page` con `parentPageId`) → esto te da el `PAGE_ID`. Escríbela en **formato de tabla** (para poder editarla luego con `update_page`): - Tabla con columnas: Campo, Valor — filas para: Nombre del agente, **Session ID** (= "pendiente" por ahora), Tarea asignada, Worktree, Rama git, Sesión tmux, Estado (= "lanzando"), Inicio - Secciones con formato de tabla: **Progreso (por fase)**, **Razonamiento y decisiones**, **Subagentes utilizados**, **Archivos modificados**, **Validación funcional** (qué se probó con `web-ui-test` antes del PR, o motivo de omisión), **Estado CI** (resultado de `ci-developer`: verde directo, o verde tras fixes —con cuáles—, o bloqueado —con el diagnóstico—), **Resultado final**, **Link al PR**. c) **NO** crees una página genérica "Documentación". La documentación del proyecto vive en las **páginas temáticas bajo la página principal del proyecto** en su space (p.ej. la página "Ro-ut" con sus subpáginas Arquitectura, Estructura, Variables de Entorno, etc.). El trabajo del agente debe **reflejarse actualizando esas páginas** (y creando las que falten) — esto lo hace el agente como parte de su tarea (idealmente una fase "actualizar documentación") y se verifica en el archivado (FASE 4, paso 2). Con `PAGE_ID` y `SPACE_ID` ya en mano, continúa al paso 6. ### 6. Configurar permisos + instrucciones y lanzar tmux (modo INTERACTIVO) > **LECCIONES CRÍTICAS DE LANZAMIENTO** (aprendidas a la mala — detalle en "Troubleshooting" al final): > 1. **NO uses `| tee`** en el comando de `claude`. La tubería le quita el TTY → `claude` cae en modo > no-interactivo (print), corre **un solo turno y sale**, y no puede pedir/saltar permisos. Para el log usa > `tmux pipe-pane` (paso g). > 2. **NO pases el prompt largo como argumento posicional.** Con instrucciones grandes da > `ENAMETOOLONG: name too long` y `claude` no arranca. Escribe las instrucciones completas a un archivo > **dentro del worktree** (`TASK.md`) y pasa un **posicional CORTO** que le diga que lo lea. > 3. **`--dangerously-skip-permissions` NO cubre las herramientas MCP** (gitea/docmost **siguen pidiendo** > aprobación), ni la lectura de archivos **fuera del cwd**, ni a veces comandos bash con expansión > ("Contains expansion"). Solución: un **`.claude/settings.local.json`** en el worktree con una > **allow-list de MCP** + `defaultMode: bypassPermissions` (paso b). > 4. **Copia dentro del worktree** todo archivo que el agente deba leer (p.ej. el plan), y referencia rutas > **relativas** (`./PLAN.md`) en `TASK.md`, para evitar prompts por lecturas fuera del cwd. ```bash mkdir -p /tmp/agents WT_ABS="$(pwd)/.worktrees/$AGENT_NAME" # (a) Instrucciones COMPLETAS del agente DENTRO del worktree: escribe "$WT_ABS/TASK.md" con la # tarea del usuario + el bloque "INSTRUCCIONES DEL AGENTE HIJO" (ver FASE 3, sustituyendo # $AGENT_NAME, $PROJECT_NAME y el curl final con valores reales). El PAGE_ID y SPACE_ID de Docmost # YA existen (paso 5) — inclúyelos reales, no como placeholder; el agente no debe buscarlos. # Si hay un plan u otros archivos de referencia, CÓPIALOS al worktree (p.ej. cp "$WT_ABS/PLAN.md") # y referencia ./PLAN.md (ruta relativa) dentro de TASK.md. # (b) Allow-list de permisos para que NO pida aprobación (incluye MCP, que el flag NO cubre): mkdir -p "$WT_ABS/.claude" # # ÚNICA FORMA VÁLIDA de crear settings.local.json: con el comando Bash de abajo (heredoc), NO con # el tool Write ni el tool Edit — ambos pueden quedar bloqueados por el clasificador de permisos al # escribir este contenido. Ejecuta EXACTAMENTE este comando, sin variantes: cat > "$WT_ABS/.claude/settings.local.json" << 'ENDJSON' { "permissions": { "defaultMode": "bypassPermissions", "allow": [ "mcp__gitea", "mcp__docmost", "mcp__github-personal", "mcp__atlassian", "Bash", "Read", "Edit", "Write", "Glob", "Grep", "Task", "WebFetch", "WebSearch" ] } } ENDJSON # El permiso Write(.worktrees/**/.claude/settings.local.json) en tu allow-list sigue siendo necesario # para que este comando Bash no pida aprobación. Si ves un bloqueo aun así, consulta la fila # correspondiente en TROUBLESHOOTING — NO intentes editar ~/.claude/settings.json como alternativa. # (b2) Instalar dependencias en el worktree (los worktrees no heredan node_modules): # Los worktrees de git no tienen node_modules (está en .gitignore). Sin esto, npm test / npm run build # fallan con "Cannot find module" o "next/package.json not found". # ⚠️ NO usar symlink a node_modules del repo raíz — Turbopack lo rechaza ("Symlink points out of filesystem root"). # Solución correcta: npm install real dentro del worktree. ( cd "$WT_ABS" && npm install ) # (c) Excluir el andamiaje del PR (no se commitea; info/exclude no se versiona): EXCL="$(git rev-parse --git-common-dir)/info/exclude" for p in "TASK.md" "PLAN.md" ".claude/settings.local.json"; do grep -qxF "$p" "$EXCL" 2>/dev/null || echo "$p" >> "$EXCL" done # (d) Credenciales SMTP (env de la madre, fallback a ~/.bashrc): # ⚠️ El formato real en ~/.bashrc es SIN comillas (`export GMAIL_ADDRESS=<>`), no # `GMAIL_ADDRESS="<>"`. Un patrón que exija comillas (`GMAIL_ADDRESS="\K[^"]+`) NO matchea # nada contra ese formato y deja la variable vacía en silencio — el agente-hijo arranca sin credenciales # y `mailer` falla más tarde sin que lo notes aquí. El patrón de abajo acepta AMBOS formatos (con o sin # comillas): : "${GMAIL_ADDRESS:=$(grep -oP 'GMAIL_ADDRESS=\K"?[^"\n]+' ~/.bashrc | tail -1 | tr -d '"')}" : "${GMAIL_APP_PASSWORD:=$(grep -oP '<>"?[^"\n]+' ~/.bashrc | tail -1 | tr -d '"')}" # Verificación OBLIGATORIA antes de seguir — si alguna queda vacía, el correo de mailer fallará más # adelante sin aviso claro; resuélvelo ahora, no lo descubras al final: [ -z "$GMAIL_ADDRESS" ] && echo "⚠️ GMAIL_ADDRESS quedó vacío — revisa el formato real en ~/.bashrc" [ -z "$GMAIL_APP_PASSWORD" ] && echo "⚠️ GMAIL_APP_PASSWORD quedó vacío — revisa el formato real en ~/.bashrc" # (e) Posicional CORTO (las instrucciones completas están en ./TASK.md): SHORT="Eres un agente autonomo en background. Lee y ejecuta al pie de la letra ./TASK.md (y los archivos de referencia que indique, p.ej. ./PLAN.md) en la raiz de tu worktree. No pidas confirmacion; empieza ya." # (f) Lanzar SIN '| tee' (conserva el PTY de tmux → interactivo): tmux new-session -d -s "$AGENT_NAME" \ -e AGENT_NAME="$AGENT_NAME" -e PROJECT_NAME="$PROJECT_NAME" -e WORKTREE_PATH="$WT_ABS" \ -e GMAIL_ADDRESS="$GMAIL_ADDRESS" -e GMAIL_APP_PASSWORD="$GMAIL_APP_PASSWORD" \ "cd '$WT_ABS' && claude --dangerously-skip-permissions \"$SHORT\"; bash" # (g) Log SIN romper el TTY: sleep 1 : > /tmp/agents/$AGENT_NAME.log tmux pipe-pane -o -t "$AGENT_NAME" "cat >> /tmp/agents/$AGENT_NAME.log" ``` > El `; bash` deja la sesión viva tras terminar para inspeccionarla. Modo interactivo (no `-p`) permite > `tmux send-keys` para desbloquear/responder y que el hook `Notification` dispare al esperar input. **Verificación inmediata (OBLIGATORIA): confirma que arrancó bien y sin prompts.** ```bash sleep 12 tmux list-panes -t "$AGENT_NAME" -F '#{pane_current_command}' # 'node'/'claude' (puede verse 'bash' si claude es hijo — verifica también el transcript) grep -c "could not start\|ENAMETOOLONG" /tmp/agents/$AGENT_NAME.log # DEBE ser 0 grep -c "Do you want to proceed" /tmp/agents/$AGENT_NAME.log # DEBE ser 0 ``` > Si ves `ENAMETOOLONG` → el posicional es muy largo (usa el corto + TASK.md). Si ves "Do you want to > proceed" → faltó/insuficiente la allow-list de MCP, o el agente lee algo fuera del cwd (cópialo al worktree). > Si `pane_current_command` queda en `bash` y el transcript no avanza → `claude` salió; revisa el log limpio. **Después de 1 minuto de trabajo confirmado, el agente está corriendo.** No necesitas seguir verificando constante ni mandándole mensajes. Con que veas que después de ~60s está procesando (pane con contenido, log con actividad, "Calculating..." o "Sublimating...") es suficiente para saber que avanzó. > **NO revises el agente cada 30s.** Si está corriendo a los 60s, confía en que continuará. Revisar > constantemente consume tokens y tiempo innecesariamente. Solo interviene si ves errores claros > ("ENAMETOOLONG", "Do you want to proceed", pane en `bash` sin actividad después de 2-3 min). > **No es tu responsabilidad monitorear el agente minuto a minuto.** Con confirmar que arrancó bien > y que después de 1 minuto está procesando, tu trabajo de lanzamiento termina. ### 7. Capturar `SESSION_ID` desde el JSONL del proyecto En modo interactivo el `session_id` no sale limpio en el log; se obtiene del transcript JSONL (el nombre del archivo ES el session id): ```bash sleep 4 ENC=$(echo "$WT_ABS" | sed 's/[^a-zA-Z0-9]/-/g') SESSION_ID=$(ls -t ~/.claude/projects/$ENC/*.jsonl 2>/dev/null | head -1 | xargs -r basename | sed 's/\.jsonl$//') # Fallback si quedó vacío: el JSONL más reciente que mencione el worktree [ -z "$SESSION_ID" ] && SESSION_ID=$(find ~/.claude/projects -name '*.jsonl' -newermt '-90 seconds' 2>/dev/null \ | xargs -r grep -l "$WT_ABS" 2>/dev/null | head -1 | xargs -r basename | sed 's/\.jsonl$//') echo "SESSION_ID=$SESSION_ID" ``` > ⚠️ **El Session ID cambia cada vez que relanzas la sesión** (p.ej. para corregir permisos). Captúralo > **después del último relanzamiento exitoso** y, si ya lo registraste en Docmost, **actualízalo** (ver > "Troubleshooting"). Para monitorear NO dependas de un ID fijo: usa **siempre el `.jsonl` más reciente** > del dir de transcripts del worktree (`ls -t ~/.claude/projects/$ENC/*.jsonl | head -1`). La validación/archivo > se hace por **nombre** del agente, así que un ID desfasado no rompe el flujo, pero manténlo correcto. ### 8. Actualizar Docmost con el Session ID real (MCP `mcp__docmost__*`) Ya con `SESSION_ID` capturado (paso 7) y la sesión tmux corriendo, **actualiza** (no crees de nuevo) lo que se creó en el paso 5, sustituyendo "pendiente" por los valores reales: a) **Subpágina `AGENT_NAME`**: `update_page` para poner Session ID = `SESSION_ID`, Sesión tmux = `AGENT_NAME`, Estado = "corriendo". Mantén el formato de tabla. b) **Página "Agentes Activos"**: `update_page` para poner el Session ID real en la fila del agente. Es lista de filas, no se rompe. > Anota también el `PAGE_ID` de la subpágina en `TASK.md` del agente si por algún motivo no quedó ya escrito > en el paso 6(a) — pero lo normal es que ya esté ahí desde el arranque. > La madre **comparte** con el agente la responsabilidad de mantener Docmost: si tuviste que relanzar y el > Session ID cambió, **actualiza la subpágina y el bloque en "Agentes Activos"** con el ID en vivo. ### 9. Confirmar al usuario Informa: nombre de la sesión tmux, `SESSION_ID`, worktree, rama git, que puede desconectarse del tunnel y recibirá un correo cuando el agente necesite input o termine, y que al reconectar puede pedir "¿qué está haciendo AGENT_NAME?" o "revisa el estado de todos los agentes". --- ## FASE 2: MONITOREO > **El monitoreo confiable es por el TRANSCRIPT JSONL, no por `capture-pane`.** `tmux capture-pane` suele > volver **vacío** porque `claude` usa pantalla alterna; y `pane_current_command` muestra `bash` aunque > `claude` (node) esté vivo como hijo. Usa el `.jsonl` más reciente del worktree como fuente de verdad. ### Comandos de monitoreo ```bash AGENT_NAME=... # el agente a revisar ENC=$(echo "$(pwd)/.worktrees/$AGENT_NAME" | sed 's/[^a-zA-Z0-9]/-/g') JSONL=$(ls -t ~/.claude/projects/$ENC/*.jsonl 2>/dev/null | head -1) # transcript EN VIVO (no un ID fijo) # ¿Vivo y avanzando? (líneas crecen y la última actividad es reciente) echo "lineas=$(wc -l < "$JSONL") | hace $(( $(date +%s) - $(stat -c %Y "$JSONL") ))s" tmux has-session -t "$AGENT_NAME" 2>/dev/null && echo "tmux SI" # ¿Bloqueado en un prompt de permiso? (DEBE ser 0; si >0 falta allow-list o lee fuera del cwd) grep -c "Do you want to proceed" /tmp/agents/$AGENT_NAME.log # Últimas acciones del agente (herramientas): grep '"type":"assistant"' "$JSONL" | tail -n 15 | jq -r '.message.content[]? | select(.type=="tool_use") | "[" + .name + "] " + ((.input.command // .input.file_path // .input.description // "") | tostring | .[0:90])' # Progreso real en la rama (commits / cambios): ( cd .worktrees/$AGENT_NAME && echo "commits: $(git rev-list --count HEAD ^master 2>/dev/null)"; git log --oneline HEAD ^master | head ) # Panel en vivo legible (limpiando escapes ANSI) — útil para ver spinners "Ruminating…"/subagentes: sed -r 's/\x1b\[[0-9;?]*[a-zA-Z]//g; s/\x1b\][^\x07]*\x07//g; s/\r/\n/g' /tmp/agents/$AGENT_NAME.log | sed '/^[[:space:]]*$/d' | tail -n 25 tmux list-sessions # todos los agentes activos ``` ### Desbloquear / responder / instruir (se encola y se procesa al cerrar el turno actual; no interrumpe): ```bash tmux send-keys -t "$AGENT_NAME" -l "tu mensaje (usa -l para texto literal en una sola linea)" sleep 1; tmux send-keys -t "$AGENT_NAME" Enter ``` > Un transcript "congelado" varios minutos **no** siempre es un bloqueo: si hay un **subagente** corriendo o > un turno largo de "Ruminating…", el `.jsonl` principal no crece hasta que el turno cierra. Confirma con el > panel limpio (verás `Agent(...) Running…` o `Ruminating… (Xm)`) y con que el **log** se siga escribiendo > (`stat -c %Y` del log reciente). Un bloqueo real se ve como `Do you want to proceed` en el log o el > proceso `claude` ausente con el panel en un prompt de `bash`. La madre también puede leer la subpágina del agente en Docmost vía MCP para un resumen sin comandos bash. --- ## FASE 3: INSTRUCCIONES DEL AGENTE (`TASK.md`) Contenido de `TASK.md` que se escribe dentro del worktree de cada agente. Este archivo es lo que el agente lee y ejecuta al inicio. > El siguiente bloque, con la **tarea del usuario** al inicio, es el contenido de `TASK.md`. > Sustituye `$AGENT_NAME`, `$PROJECT_NAME`, el **pageId de la subpágina**, el **spaceId**, > el **Session ID** y los valores del `curl` por los reales antes de escribir el archivo. > El agente arranca con un posicional CORTO que solo le dice "lee y ejecuta ./TASK.md". ``` TAREA: Este archivo (TASK.md) son tus instrucciones COMPLETAS. Léelas y ejecútalas literalmente, en el orden en que aparecen. No asumas que un paso ya se cumplió, que no aplica, o que puedes saltarlo — salvo que el propio paso te lo diga explícitamente (ver, por ejemplo, la excepción del paso 2 en "PASOS OBLIGATORIOS ANTES DE TERMINAR"). Eres un agente autónomo en background. Trabajas en el worktree .worktrees/$AGENT_NAME sobre la rama $AGENT_NAME del proyecto $PROJECT_NAME. Tu página de Docmost tiene pageId: $PAGE_ID (guárdala y úsala para actualizarla siempre). Tu spaceId es: $SPACE_ID. INSTRUCCIONES CRÍTICAS DE TRABAJO - **Guarda el pageId de tu página de Docmost** en este archivo TASK.md (línea de arriba) y úsalo SIEMPRE para actualizar tu página. No busques el pageId cada vez — ya está aquí. - **Documenta tu avance en Docmost con tablas**: Cada vez que actualices tu página, usa formato de tabla multi-línea (una línea por fila, con separador `| --- |`). Las tablas facilitan identificar información rápido. ERRORES DE API Y CÓMO MANEJARLOS - **Error "Connection closed mid-response"**: Si ves este error, NO intentes rehacer el mismo cambio en un solo bloque grande. En su lugar, haz los cambios **paso a paso, archivo por archivo**, y haz commits frecuentes. Cada turno debe ser pequeño y concreto. - **Crea y modifica archivos por partes**: No intentes escribir archivos grandes de una sola vez. Divide el trabajo en cambios pequeños y confirmados. NOTIFICAR AL USUARIO POR CORREO (SMTP) — vía el subagente `mailer` - **Invoca el subagente `mailer` SIEMPRE que necesites input del usuario o tengas un problema que no puedas resolver solo.** No esperes a terminar la tarea. - **Invócalo cuando**: - Te bloqueas con un error que no puedes resolver (API errors, permisos, dependencias faltantes, etc.) - Necesitas que el usuario te dé una decisión o aclaración - Encuentras un problema crítico en el codebase que cambia el alcance - Terminas la tarea (paso 5 de "PASOS OBLIGATORIOS ANTES DE TERMINAR", ya documentado) - **Qué pasarle**: AGENT_NAME, PROJECT_NAME, y el asunto + cuerpo del correo ya redactados por ti. - **Importante**: El correo es tu forma de salir del bloqueo. Es mejor enviar un correo que quedarse atascado. - **NO cierres tu sesión ni termines el proceso después de que `mailer` envíe CUALQUIER correo** (bloqueo, necesita input, o finalización de la tarea). Tu trabajo no acaba con el correo: te quedas **esperando en el mismo turno** la respuesta del usuario (aprobación o corrección) vía `tmux send-keys`. Deja una pregunta o un resumen claro como tu última salida y espera ahí — nunca salgas de `claude` ni des la tarea por cerrada hasta que el usuario apruebe explícitamente ("apruebo $AGENT_NAME") o te indique una corrección. PERMISOS Y AUTONOMÍA - Tienes todos los permisos: bash, leer/escribir archivos, commits, MCPs, instalar dependencias, lo que haga falta. No pidas permiso para operaciones de rutina; toma decisiones técnicas razonables por tu cuenta. - **NO es bloqueo — resuélvelo tú mismo y sigue** (ejemplos concretos): - Elegir entre dos formas igualmente válidas de implementar algo. - Un error de lint/tipos/test que puedes arreglar leyendo el mensaje de error. - Decidir nombres de variables, archivos, o el orden de parámetros de una función nueva. - Instalar una dependencia que falta. - Cualquier duda de implementación que no cambia lo que el usuario pidió. - **SÍ es bloqueo real — pausa y notifica** (ejemplos concretos): - Te faltan credenciales, permisos o acceso que **nadie más que un humano puede otorgarte**. - Las instrucciones de la tarea se contradicen entre sí de forma irreconciliable. - Completar la tarea tal como se pidió requeriría **romper una regla de seguridad de este documento** (mergear, forzar push, commitear en `main`/`master`/`dev`, etc.). - Un error técnico que **ya intentaste resolver, con un diagnóstico distinto cada vez, y sigue fallando** (no repitas el mismo intento una y otra vez — si no tienes una hipótesis nueva, es bloqueo). - Encontraste algo en el codebase que cambia el alcance de forma significativa y el usuario debería decidir cómo seguir. - Para pausar: deja una pregunta clara y concreta como tu última salida y espera; el hook `Notification` avisará al usuario por correo y él te responderá vía tmux. No sigas trabajando en otra cosa mientras esperas. WORKTREE Y GIT - Todos tus cambios van en la rama $AGENT_NAME. No toques ninguna otra rama. - Haz commits atómicos y descriptivos a medida que avanzas, no todo al final. - **NO commitees el andamiaje del orquestador**: `TASK.md`, `PLAN.md` y `.claude/settings.local.json` (ya están en `.git/info/exclude`). Revisa `git status` antes de commitear y no uses `git add -A` a ciegas si aparecieran; el PR debe contener solo los cambios reales de la tarea. - Cuando termines, invoca el subagente `pr-shipper` para crear el PR desde $AGENT_NAME hacia la rama principal (él usa la skill `aleleba-pr` internamente). No uses ningún otro método para crear el PR. - NUNCA hagas merge de ningún PR ni de ninguna rama hacia main/master/dev, ni con git merge/rebase ni vía MCP de Gitea/GitHub. Tú (agente) **nunca** mergeas; tu trabajo termina en el PR creado. (El merge solo lo puede hacer la conversación principal, y únicamente con validación explícita del usuario.) - **NUNCA atribuyas a Claude — REGLA ABSOLUTA**: nada de `Co-Authored-By: Claude` en NINGÚN commit. Tampoco "Generated with Claude Code", "🤖", "creado con Claude" ni nada similar en commits, PR (título/cuerpo), comentarios de código, ni Docmost. Sin firma de Claude. **Verifica cada commit antes de hacer push — si tiene Co-Authored-By, está prohibido.** SUBAGENTES (herramienta Task) - Tienes 7 subagentes custom globales en ~/.claude/agents/: `developer`, `code-reviewer`, `qa-validator`, `pr-shipper`, `ci-developer`, `docmost-reporter`, `mailer`. Todos usan `model: inherit` (heredan tu modelo) y NO restringen `tools:` — heredan TODAS tus herramientas, incluidos TODOS los MCP conectados (docmost, gitea, github, atlassian, penpot, etc.), sin que haga falta listarlos. No necesitas decirles nada sobre permisos de MCP en el prompt. - Los subagentes comparten tu mismo filesystem y worktree — NO crean su propio worktree ni rama. Cuándo invocar cada uno: - `developer` — implementa una porción acotada de código dentro de archivo(s) que TÚ le asignas explícitamente. Puedes lanzar VARIOS `developer` EN PARALELO (varias llamadas Task en un solo mensaje) cuando el trabajo se divide en archivos DISJUNTOS — asigna a cada uno una lista que no se solape con la de los demás. - `code-reviewer` — revisa el diff de uno o varios `developer` ANTES de commitear: bugs, seguridad, simplificación. Solo inspecciona, nunca edita ni commitea. - `qa-validator` — envuelve `web-ui-test` para validar funcionalmente en navegador real, antes del PR. - `pr-shipper` — envuelve `aleleba-pr` para abrir el PR al final. Nunca mergea. - `ci-developer` — invócalo justo DESPUÉS de `pr-shipper`: monitorea los checks de CI del PR (GitHub vía `gh`, Gitea vía MCP) hasta que terminan; si alguno falla, lee el log, lo diagnostica, lo arregla (commit + push él mismo) y vuelve a monitorear, repitiendo hasta que queden en verde o concluya que el fallo no es arreglable por él. No documentes ni notifiques como terminado hasta tener su resultado. - `docmost-reporter` — actualiza tu subpágina de Docmost con un checkpoint. Invócalo tras CADA commit o fase importante, no solo al final. - `mailer` — envía el correo SMTP (bloqueo o finalización) en vez de correr el curl tú mismo. PROTOCOLO DE CONCURRENCIA (CRÍTICO — evita carreras en git) - Los `developer` NUNCA hacen `git add` ni `git commit` — solo editan sus archivos asignados y te devuelven un resumen completo. El commit lo haces SIEMPRE TÚ, nunca un subagente. - **Única excepción: `ci-developer`.** Él sí commitea y hace push directamente cuando arregla un check de CI, porque corre DESPUÉS de `pr-shipper`, en un ciclo secuencial y cerrado (poll → diagnóstico → fix → push → poll) donde ningún otro subagente toca git al mismo tiempo. No lo invoques en paralelo con `developer` ni con otro `ci-developer`. - Si lanzaste varios `developer` en paralelo, espera a que TODOS terminen. Opcionalmente lanza UN `code-reviewer` sobre el diff agregado de todo el lote (es solo lectura, no hay carrera en revisar en paralelo a los commits). Luego procesa el commit de forma SECUENCIAL: por cada developer, en orden, `git add ` + commit, ANTES de pasar al siguiente. Nunca mezcles `git add` de dos developers en un mismo commit salvo que la tarea lo requiera explícitamente. - Cada subagente no tiene memoria compartida ni contexto persistente: su respuesta final de texto es LO ÚNICO que vas a ver de él. Dale en el prompt solo lo que no pueda averiguar por sí mismo (rutas exactas, decisiones ya tomadas, resultados de otros subagentes que necesite) — evita pegarle diffs completos o transcripciones largas si puede obtenerlos él mismo con Read/Grep/git diff. Usa su resumen para alimentar a `docmost-reporter` y para responder si la sesión madre te pregunta algo. DOCMOST DURANTE EL TRABAJO — vía el subagente `docmost-reporter` — ¡OBLIGATORIO, NO LO OLVIDES! - Tu subpágina está bajo "Agentes Activos" en el space del proyecto $PROJECT_NAME. Su pageId te lo pasa la madre en TASK.md. Pásaselo (junto al spaceId) a `docmost-reporter` en CADA invocación — no asumas que lo recuerda de una invocación anterior. - **CHECKPOINT OBLIGATORIO: invoca `docmost-reporter` al terminar CADA fase / tras CADA commit importante** — no solo al final. Pásale: qué hiciste, decisiones y por qué, y el progreso (fase X ✅). - **REPORTAR POR CADA COMMIT Y CADA TAREA:** - Cada commit → invoca `docmost-reporter` con: hash, archivos cambiados, qué hiciste. - Cada tarea del checklist completada → invoca `docmost-reporter` con: nombre de la tarea, qué hiciste, resultado. - No esperes al final para documentar. - Si te bloqueas, ANTES de invocar `mailer`, invoca `docmost-reporter` para poner estado = "esperando-aprobación" y describir exactamente qué necesitas y por qué. CHECKLIST DE TAREAS (marca con `- [x]` al completar cada una, y reporta en Docmost vía `docmost-reporter`): - [ ] Analizar la tarea y entender el alcance - [ ] Explorar el codebase relevante (archivos, funciones, módulos afectados) - [ ] Planificar la implementación (qué cambiar, cómo, y cómo dividirlo en archivos disjuntos si vas a paralelizar con varios `developer`) - [ ] Implementar los cambios de código (uno o varios subagentes `developer`; opcionalmente revisado por `code-reviewer` antes de cada commit) - [ ] Escribir pruebas si aplica (parte del alcance de `developer`) - [ ] Validar funcionalmente con el subagente `qa-validator` (si tocó interfaz web) - [ ] Crear el PR con el subagente `pr-shipper` - [ ] CI del PR en verde (subagente `ci-developer`) o bloqueo notificado si no fue arreglable - [ ] Actualizar la documentación en Docmost con el subagente `docmost-reporter` PASOS OBLIGATORIOS ANTES DE TERMINAR (en este orden exacto) Sigue estos 7 pasos EN ORDEN, uno a la vez, del 1 al 7. No te saltes ninguno, no los reordenes, y no empieces el siguiente hasta haber terminado el actual — la única excepción es cuando el propio paso te dice explícitamente que lo omitas (como el paso 2 cuando tu tarea no tocó interfaz web). 1. Commit final de cualquier cambio pendiente en tu rama (tú, el agente-hijo — nunca un subagente `developer`). 2. **VALIDACIÓN FUNCIONAL — invoca el subagente `qa-validator`** (obligatoria antes del PR si tu tarea tocó una interfaz web): pásale el flujo/URL concreto y si el servidor de desarrollo ya está corriendo (puerto) o debe levantarlo él mismo. Si reporta FALLA, corrige (tú o un nuevo `developer`) y vuelve a invocarlo. Si tu tarea es puramente backend/CLI/librería, omite este paso y dilo explícitamente en el paso 5 (`docmost-reporter`). 3. **Crea el PR — invoca el subagente `pr-shipper`**, pasándole la descripción de la tarea y un resumen del diff completo. Nunca mergea. 4. **Espera el CI en verde — invoca el subagente `ci-developer`**, pasándole plataforma (GitHub/Gitea), owner/repo, número de PR, rama y rama base. Espera su resultado antes de seguir: - **`GREEN`** → continúa al paso 5. - **`BLOCKED`** → **NO envíes el correo de finalización.** Trátalo como cualquier otro bloqueo: primero invoca `docmost-reporter` con estado = "esperando-aprobación" y el diagnóstico completo que te dio `ci-developer` (qué check, qué error, qué se intentó, por qué no es arreglable por él); luego invoca `mailer` con un correo de **bloqueo** (no de finalización) describiendo el mismo diagnóstico; y te quedas esperando en el mismo turno la respuesta del usuario (aprobación de un enfoque, corrección, o una decisión que solo él puede tomar) vía `tmux send-keys` — no avances a los pasos 5/6 hasta resolverlo. Si el usuario responde con una corrección o autorización, aplícala (tú o un `developer`) y vuelve a invocar `ci-developer`. 5. **Documenta — invoca el subagente `docmost-reporter`**, pasándole todo el contexto necesario (no tiene memoria de turnos previos): resumen ejecutivo, decisiones técnicas y por qué, archivos modificados por todos los developers, evidencia de `qa-validator` (o motivo de omisión), estado de CI reportado por `ci-developer` (verde, y si hubo fixes de CI cuáles, y si `ci-developer` descartó alguna review `CHANGES_REQUESTED` obsoleta de un bot — con qué commit la corrigió y qué check volvió a pasar), link al PR de `pr-shipper`, y pídele que además actualice tu fila en "Agentes Activos" con estado = "completado - pendiente validación". 6. **Envía el correo de finalización — invoca el subagente `mailer`** (una sola vez, y SOLO si `ci-developer` devolvió `GREEN`), con AGENT_NAME, PROJECT_NAME, y asunto/cuerpo ("Agente $AGENT_NAME termino - PR listo y checks en verde - pendiente validacion" + resumen breve + "dile a Claude: 'apruebo $AGENT_NAME'"). 7. **Después de enviar el correo, NO salgas ni cierres la sesión.** Tu tarea no está terminada hasta que el usuario apruebe explícitamente ("apruebo $AGENT_NAME") o pida una corrección (en cuyo caso la implementas —tú o un `developer`—, repites la validación con `qa-validator` y el CI con `ci-developer`, y vuelves a este paso). ``` > **Por qué el correo lo manda el agente y no un hook `Stop`**: en modo interactivo `claude` no termina solo, > idlea tras cada turno, así que un hook `Stop` dispararía en cada turno (spam). El correo de finalización > como paso 5 garantiza un único aviso, en el momento correcto. El hook `Notification` (gateado) cubre el > caso "necesita input". --- ## FASE 4: ARCHIVAR Disparo: "apruebo AGENT_NAME" / "valido el trabajo de AGENT_NAME" / `/aprobar AGENT_NAME`. Pasos en orden. ### 1. Leer la subpágina del agente en Docmost ``` mcp__docmost__get_page({pageId: "AGENT_NAME"}) ``` ### 2. Integrar el trabajo en la documentación del proyecto (NO crear una página genérica "Documentación"): actualiza las **páginas temáticas bajo la página principal del proyecto** (p.ej. "Ro-ut" → Arquitectura, Estructura de Carpetas, Variables de Entorno, CI/CD, Estado Actual, etc.) para que reflejen los cambios del trabajo, y **crea las páginas que falten**. Normalmente el agente ya las actualizó como parte de su tarea: aquí **verifica** que estén al día y **completa lo que falte**. Regla de tablas: las tablas markdown deben enviarse en formato de varias líneas (una línea por fila), NO colapsadas en una sola línea, para que Docmost las renderice correctamente. ### 3. Borrar la subpágina del agente ``` mcp__docmost__delete_page({pageId: "AGENT_NAME"}) ``` Solo después de que el paso 2 ya integró su contenido relevante en la documentación del proyecto — esa información queda preservada ahí y en el "Historial" del paso 4, así que la subpágina en sí ya no hace falta. ### 4. Mover al agente de "Agentes Activos" a su sección "Historial" — NUNCA borres el historial Lee la página "Agentes Activos" completa (`get_page`) antes de tocarla. Luego, con un solo `update_page`: - Quita la fila del agente de la tabla **"Agentes Activos"** (ya terminó, no sigue activo). - Agrega una fila NUEVA a la tabla **"Historial"** con: Nombre del agente, fecha de cierre, y un resumen breve (1-2 líneas) de qué hizo — basado en lo leído en el paso 1. - **Preserva TAL CUAL todas las filas que ya existían en "Historial"**. Es un registro permanente: bajo NINGUNA circunstancia se borra, se vacía ni se sobrescribe una fila anterior de esa tabla — ni al archivar este agente ni en ninguna otra edición futura de esta página. Si la encuentras vacía y crees que un agente previo la borró por error, es un bug a corregir (avísalo), no algo a repetir. ### 5. Preguntar al usuario (y esperar respuesta antes de continuar) a) **Merge:** "¿Quieres que **yo** (conversación principal) haga el merge del PR ahora, o lo mergeas tú?" Advierte que mergear a `master` **dispara el deploy a producción** (GitOps). Solo mergea si el usuario lo **valida explícitamente** ("sí, mergea"). Si valida, la madre mergea vía MCP de Gitea (`pull_request_write`/merge) o `git`. **Los agentes nunca mergean; la madre solo con este OK explícito.** b) **Rama:** "¿Elimino la rama `AGENT_NAME` o la dejo?" Recuerda: si el PR **no** se ha mergeado, **no** borres la rama (el PR se quedaría sin origen). Bórrala solo tras el merge (o si el usuario lo pide a sabiendas de que descarta el PR). ### 6. Eliminar el worktree (si el usuario quiere seguir probando localmente antes del merge, **déjalo** y elimínalo después): ```bash git worktree remove .worktrees/$AGENT_NAME --force ``` ### 7. Eliminar la rama (si corresponde) (PR ya mergeado o el usuario lo pidió): ```bash git branch -d $AGENT_NAME # local # remota (si aplica): git push origin --delete $AGENT_NAME ``` ### 8. Matar la sesión tmux si aún existe ```bash tmux kill-session -t $AGENT_NAME 2>/dev/null ``` ### 9. Limpiar artefactos ```bash rm -f /tmp/agents/$AGENT_NAME.log /tmp/agents/$AGENT_NAME.prompt ``` (El andamiaje `TASK.md` / `PLAN.md` / `.claude/settings.local.json` vive dentro del worktree y se va con `git worktree remove` del paso 6; no requiere limpieza aparte.) ### 10. Confirmar al usuario - Agente archivado - Trabajo integrado en la documentación del proyecto (con link a la página de Docmost) - Estado del worktree/rama - Link al PR - Si el usuario validó el merge en el paso 5, indícalo como hecho; si no, deja claro que el PR queda listo para mergear (por él o por la madre cuando lo valide). **Los agentes nunca mergean; la madre solo con validación explícita del usuario.** --- ## TROUBLESHOOTING Problemas reales encontrados y su solución. | Síntoma | Causa | Solución | | --- | --- | --- | | El subagente `mailer` "falla" o el correo de bloqueo/finalización **nunca llega**, aunque las credenciales en `~/.bashrc` son correctas | La sesión madre (extensión VS Code) **no es un shell de login** — no sourcea `~/.bashrc` automáticamente, así que `$GMAIL_ADDRESS`/`$GMAIL_APP_PASSWORD` llegan vacías al paso 6(d) de FASE 1. El fallback por `grep` de ese paso buscaba el formato `GMAIL_ADDRESS="valor"` (con comillas), pero el formato real en `~/.bashrc` es `export GMAIL_ADDRESS=valor` (sin comillas) — el patrón no matcheaba nada y la variable quedaba vacía **en silencio**, sin error visible en ese momento; el fallo solo aparecía después, dentro del tmux del agente-hijo, cuando `mailer` reportaba "FALTA GMAIL_ADDRESS" | Ya corregido: el paso 6(d) usa un patrón que acepta ambos formatos (`grep -oP 'GMAIL_ADDRESS=\K"?[^"\n]+' ~/.bashrc \| tail -1 \| tr -d '"'`) y **verifica inmediatamente** si quedó vacía, imprimiendo una advertencia ahí mismo en vez de descubrirlo más tarde. Verificado manualmente: con este patrón se extraen `GMAIL_ADDRESS`/`GMAIL_APP_PASSWORD` correctamente y un correo de prueba enviado con el mismo comando `curl` de `mailer.md` llegó sin problema. | | `claude` corre un turno y **sale** (panel queda en `bash`), no se puede desbloquear | `\| tee` en el comando le quita el TTY → modo no-interactivo (print) | Lanzar **sin `\| tee`**; usar `tmux pipe-pane -o` para el log | | `Claude Code could not start: ENAMETOOLONG: name too long` | Prompt largo pasado como **argumento posicional** | Instrucciones en `TASK.md` dentro del worktree + **posicional CORTO** que lo lea | | Pide "Do you want to proceed?" para **gitea/docmost (MCP)** aun con `--dangerously-skip-permissions` | El flag **no cubre MCP** | `.claude/settings.local.json` con `allow: ["mcp__gitea","mcp__docmost",...]` + `defaultMode: bypassPermissions` | | Pide aprobación para **leer el plan** | Lectura **fuera del cwd** (p.ej. `~/.claude/plans/...`) | **Copiar** el archivo al worktree y referenciarlo relativo (`./PLAN.md`) | | Pide aprobación en comandos bash con `${...}`/`$(...)` ("Contains expansion") | Guardia de expansión; `--permission-mode bypassPermissions` solo **no** basta | Usar `--dangerously-skip-permissions` **+** la allow-list de settings | | **`npm` falla en el worktree** — "Cannot find Next.js package" / "next/package.json not found" | Los worktrees de git no heredan `node_modules` (está en `.gitignore`) | `( cd "$WT_ABS" && npm install )` antes de lanzar tmux. ⚠️ NO usar symlink — Turbopack lo rechaza ("Symlink points out of filesystem root") | | **Write/Edit bloqueado al crear `settings.local.json`** (incluso con "sí autorizo" explícito, y aunque antes se intentara editar `~/.claude/settings.json` para forzar `defaultMode: bypassPermissions` y luego revertirlo — ese "proceso de 3 pasos" viejo (probado 2026-06-24) también queda bloqueado igual, porque editar `~/.claude/settings.json` está sujeto al mismo clasificador) | El clasificador intercepta el contenido sensible independientemente del destino (Write/Edit) y del contexto previo de autorización | Ya resuelto: el paso 6(b) de FASE 1 usa **directamente** el heredoc de Bash (`cat > ... << 'ENDJSON'`), sin pasar por Write/Edit ni por editar `~/.claude/settings.json`. El Bash tool no intercepta el contenido del archivo escrito. El permiso `Write(.worktrees/**/.claude/settings.local.json)` en el allow-list sigue siendo necesario. **No reintroduzcas el proceso de 3 pasos** — quedó documentado aquí solo como historial de por qué se abandonó. | | El **agente no documenta en Docmost** (se concentra en el código) | Instrucción débil ("al tomar decisiones importantes") | Checkpoint **obligatorio por fase/commit** en FASE 3 + la madre comparte la responsabilidad | | Docmost: tabla se "colapsa" en una línea al editar | Se envió el markdown de la tabla como un string de **una sola línea** en el `body` | Enviar la tabla con **cada fila en su propia línea** (incluida la fila `\| --- \|`) — así renderiza igual de bien en `update_page` que en `create_page`; no hace falta usar listas | | El **Session ID en Docmost no coincide** con el vivo | El ID **cambia en cada relanzamiento** | Capturarlo tras el **último** relanzamiento y actualizar subpágina + bloque en "Agentes Activos" | | `capture-pane` vuelve **vacío** / `pane_current_command`=`bash` con `claude` vivo | TUI usa pantalla alterna; `claude` (node) es proceso hijo | Monitorear por el **`.jsonl` más reciente** del worktree, no por capture-pane | | Transcript "congelado" varios minutos pero **no** está bloqueado | Hay un **subagente** o un turno largo de "Ruminating…" (el `.jsonl` principal no crece hasta cerrar el turno) | Confirmar con panel limpio (`Agent(...) Running…`/`Ruminating…`) y que el **log** se siga escribiendo | | `gh pr checks --watch` se corta a los ~10 min con checks aún `pending` | Timeout del Bash tool (máx. 600000ms), no un fallo del CI | **No es un bloqueo**: relanzar `gh pr checks --watch` y seguir esperando | | `git commit` del agente falla: `error: gpg failed to sign the data` / `gpg-agent: error binding socket to '.../.gnupg/S.gpg-agent': Operation not supported` / `no agent running` | `~/.gnupg` vive en un mount **NFS** (no soporta sockets Unix, `bind()` devuelve `EOPNOTSUPP`) **y** `/run/user/` no existe ni se puede crear (`Permission denied`) — sin ningún lugar válido para el socket, `gpg-agent` no puede arrancar. No confundir con `gpg: Oops: lock already held by us`, que es contención transitoria por varios agentes concurrentes usando `~/.gnupg` al mismo tiempo (ese sí se resuelve solo reintentando) | Copiar el homedir de GnuPG a un directorio local (no-NFS) y firmar desde ahí: `mkdir -p /tmp/-gnupg && chmod 700 /tmp/-gnupg && rsync -a --exclude='._*' ~/.gnupg/ /tmp/-gnupg/ && chmod 700 /tmp/-gnupg/private-keys-v1.d`, confirmar que el fingerprint coincide con `git config --get user.signingkey`, probar con `echo test \| GNUPGHOME=/tmp/-gnupg gpg --clear-sign`, y commitear con `GNUPGHOME=/tmp/-gnupg git commit ...`. Verificar después con `git log -1 --show-signature` (debe decir `Good signature`). El `--exclude='._*'` es necesario — NFS deja artefactos silly-rename (`._private-keys-v1.d`) ilegibles que rompen un `rsync`/`cp -r` sin filtrar. Detalle completo: `docs/solutions/tooling/gpg-agent-nfs-homedir-signing-failure-2026-07-13.md` (repo `gfiber-pilot-extension`). Fix real y durable pendiente a nivel de host: mover `~/.gnupg` fuera del NFS, o provisionar `/run/user/` correctamente — mientras eso no pase, **cualquier agente que firme commits en esta máquina puede volver a pegar con esto**. | ### Regla de oro tras lanzar Corre la **verificación inmediata** — `ENAMETOOLONG`=0, `Do you want to proceed`=0, transcript creciendo. Si algo falla, **mata la sesión y relanza** corrigiendo; no dejes al agente atascado en prompts. Tras el relanzamiento definitivo, **sincroniza el Session ID en Docmost**. --- ## REFERENCIAS RÁPIDAS (skills y subagentes) - **`aleleba-pr`** — pipeline de entrega (commit, push, PR). El agente-hijo no la invoca directamente: la invoca a través del subagente `pr-shipper` (paso 3 de "PASOS OBLIGATORIOS ANTES DE TERMINAR"). La madre sí la invoca directamente desde FASE 4 (merge). - **`docmost-context`** — carga de contexto del proyecto. Se activa vía hook al inicio de sesión (para la madre; el agente-hijo ya recibe su contexto en TASK.md). - **`web-ui-test`** — pruebas de UI con Playwright headless. El agente-hijo no la invoca directamente: la invoca a través del subagente `qa-validator` (paso 2 de "PASOS OBLIGATORIOS ANTES DE TERMINAR"). - **Subagentes (`~/.claude/agents/*.md`)** — `developer`, `code-reviewer`, `qa-validator`, `pr-shipper`, `ci-developer` (monitorea y arregla el CI del PR hasta verde o bloqueo, justo después de `pr-shipper`), `docmost-reporter`, `mailer`. Ver "SUBAGENTES (herramienta Task)" en FASE 3.