--- name: clip-health description: Diagnóstico rápido Paperclip — serviço, BD, heartbeats falhados, budget, disco. Usar quando "clip health", "saúde clip", "diagnóstico paperclip", "clip problemas". context: fork --- # /clip-health — Diagnostico Paperclip Verificacao rapida da saude do sistema Clip. ## Constantes ``` BD: PGPASSWORD="paperclip" psql -h localhost -p 54329 -U paperclip -d paperclip PAPERCLIP_DIR: /home/ealmeida/paperclip INSTANCE_DIR: /home/ealmeida/.paperclip/instances/default ``` ## Procedimento Executar todas as verificacoes em paralelo, depois apresentar relatorio. ### Check 1: Serviço ```bash ps aux | grep -E "paperclip.*src/index|pnpm.*paperclipai" | grep -v grep | head -5 ``` - Processos encontrados = OK - Nenhum processo = CRITICO (iniciar com: `cd /home/ealmeida/paperclip && pnpm --filter @paperclipai/server dev`) Verificar também memória partilhada PostgreSQL (causa do bug POSIX shared memory): ```bash ls /dev/shm/PostgreSQL.* 2>/dev/null && echo "POSIX OK" || echo "POSIX em falta — BD pode falhar ao ligar" ``` - POSIX OK = OK - POSIX em falta + processos activos = AVISO (PostgreSQL perdeu shared memory — reiniciar Paperclip) - POSIX em falta + sem processos = CRITICO ### Check 2: Base de dados Conexao: ```sql SELECT 1 as connected; ``` Tamanho: ```sql SELECT pg_size_pretty(pg_database_size('paperclip')) as db_size; ``` Contagens: ```sql SELECT (SELECT COUNT(*) FROM agents WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as agentes, (SELECT COUNT(*) FROM issues WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as issues, (SELECT COUNT(*) FROM heartbeat_runs hr JOIN agents a ON hr.agent_id = a.id WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f') as heartbeats; ``` - Conexao OK = OK - Conexao falha = CRITICO ### Check 3: Heartbeats falhados (24h) ```sql SELECT a.name, hr.status, LEFT(hr.error, 70) as erro, hr.started_at::timestamp(0) FROM heartbeat_runs hr JOIN agents a ON hr.agent_id = a.id WHERE a.company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f' AND hr.status IN ('failed','error') AND hr.started_at > NOW() - INTERVAL '24 hours' ORDER BY hr.started_at DESC LIMIT 20; ``` - 0 falhas = OK - 1-2 falhas = AVISO - 3+ falhas = CRITICO ### Check 4: Budget ```sql SELECT name, budget_monthly_cents, spent_monthly_cents, CASE WHEN budget_monthly_cents > 0 THEN ROUND(spent_monthly_cents::numeric / budget_monthly_cents * 100, 1) ELSE 0 END as pct FROM agents WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f' AND budget_monthly_cents > 0 AND spent_monthly_cents > 0 ORDER BY pct DESC; ``` - <80% = OK - 80-95% = AVISO - >95% = CRITICO ### Check 5: Disco ```bash df -h /home/ealmeida/.paperclip/ | tail -1 ``` ```bash du -sh /home/ealmeida/.paperclip/instances/default/db/ 2>/dev/null du -sh /home/ealmeida/.paperclip/instances/default/logs/ 2>/dev/null du -sh /home/ealmeida/.paperclip/instances/default/data/ 2>/dev/null ``` - >30% livre = OK - 15-30% livre = AVISO - <15% livre = CRITICO ### Check 6: API Health ```bash curl -s --max-time 5 http://localhost:3100/health 2>/dev/null || echo "FALHA" ``` Esperado: `{"status":"ok","version":"...","deploymentMode":"authenticated","bootstrapStatus":"ready"}` - `status: ok` = OK - `{"error":"Board access required"}` = AVISO (API responde mas sem auth — BD operacional) - FALHA/timeout = CRITICO (servidor não responde) ### Check 7: Gateway MCPs ```bash curl -s --max-time 5 https://gateway.descomplicar.pt/health 2>/dev/null || echo "FALHA" ``` - Responde = OK - Timeout/falha = AVISO ### Check 8: Referencias orfas na BD Budget policies e incidents a referenciar entidades inexistentes: Invocar tool MCP: `mcp__paperclip__diag_budget_orphans` - 0 orfaos = OK - 1+ orfaos = CRITICO (causa "Agent not found" no dashboard, apagar com DELETE) ### Check 9: Instancias duplicadas ```bash ss -tlnp | grep -E "310[0-9]" | wc -l ``` - 1 instancia = OK - 2+ instancias = CRITICO (processos orfaos do pnpm, matar com `kill -9` os PIDs com PPID=1) Detalhe dos processos: ```bash ss -tlnp | grep -E "310[0-9]" ``` ### Check 10: Porta vs Cloudflare Tunnel ```bash TUNNEL_PORT=$(grep -A1 "clip.descomplicar.pt" ~/.cloudflared/config.yml | grep service | grep -oP ':\K[0-9]+') ACTUAL_PORT=$(ss -tlnp | grep -E "310[0-9]" | head -1 | grep -oP ':310[0-9]' | tr -d ':') echo "Tunnel: $TUNNEL_PORT | Servidor: $ACTUAL_PORT" ``` - Iguais = OK - Diferentes = CRITICO (tunnel a apontar para porta errada, dashboard inacessivel) ### Check 11: Agentes sem permissoes bash Invocar tool MCP: `mcp__paperclip__diag_agents_without_skip_permissions` - 0 = OK - 1+ = CRITICO (agentes vao ficar bloqueados ao executar bash — corrigir com `/clip-agent`) ### Check 12: Agentes sem heartbeat Invocar tool MCP: `mcp__paperclip__diag_agents_missing_heartbeat` - 0 = OK - 1+ = AVISO (agentes nao acordam — podem nao processar issues) ### Check 13: Routine triggers com kind errado ou next_run_at NULL Invocar tool MCP: `mcp__paperclip__diag_routine_triggers_broken` - 0 = OK - 1+ = CRITICO (routines nao vao disparar — kind deve ser 'schedule' e next_run_at populado) ### Check 14: Issues PUBLICAR NOTICIA sem assignee ```sql SELECT COUNT(*) FROM issues WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f' AND title LIKE 'PUBLICAR NOTICIA%' AND status = 'todo' AND assignee_agent_id IS NULL; ``` - 0 = OK - 1+ = AVISO (cron assign-copywriter.sh devia atribuir a cada 5 min — verificar crontab) ### Check 15: Qualidade artigos publicados (ultimas 24h) Verificar artigos recentes no WordPress via SSH (server): ```bash cd /home/ealmeida/public_html && wp --allow-root post list --post_type=post --post_status=publish --category=noticias --date_query='{"after":"1 day ago"}' --fields=ID,post_title --format=csv 2>/dev/null ``` Para CADA artigo recente, verificar: **15a. Acentuacao** — procurar palavras sem acento: ```bash cd /home/ealmeida/public_html && wp --allow-root post get POST_ID --field=post_content 2>/dev/null | sed 's/<[^>]*>//g' | grep -oP '\b\w+cao\b|\b\w+sao\b|\bnao\b|\b\w+vel\b' | head -20 ``` Palavras terminadas em "cao", "sao" (sem til) ou "nao" indicam acentuacao em falta. **15b. Links HTML sem aspas:** ```bash cd /home/ealmeida/public_html && wp --allow-root post get POST_ID --field=post_content 2>/dev/null | grep -oP 'href=[^"'\''"][^ >]+' | head -10 ``` Se output nao vazio = links sem aspas. **15c. Titulo com maiusculas indevidas:** ```bash cd /home/ealmeida/public_html && wp --allow-root post get POST_ID --field=post_title 2>/dev/null ``` Verificar: so primeira palavra e nomes proprios devem ter maiuscula. - 0 problemas = OK - 1-3 = AVISO (artigos com defeitos cosmeticos) - 4+ = CRITICO (Copywriter nao esta a cumprir quality gates — actualizar AGENTS.md) ### Check 16: Cron assign-copywriter activo ```bash crontab -l | grep assign-copywriter ``` - Presente = OK - Ausente = CRITICO (issues PUBLICAR NOTICIA nunca serao atribuidas ao Copywriter) ### Check 17: Pipeline noticias end-to-end (ultimas 24h) ```sql -- Routines disparadas SELECT COUNT(*) as routines_fired FROM routine_triggers rt JOIN routines r ON rt.routine_id = r.id WHERE r.title LIKE 'Pesquisa diaria%noticias%' AND rt.last_fired_at > NOW() - INTERVAL '24 hours'; -- Issues de pesquisa concluidas SELECT COUNT(*) as pesquisas_done FROM issues WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f' AND title LIKE 'Pesquisa diaria%' AND status = 'done' AND created_at > NOW() - INTERVAL '24 hours'; -- Issues PUBLICAR NOTICIA criadas e concluidas SELECT COUNT(*) as total, COUNT(*) FILTER (WHERE status = 'done') as done, COUNT(*) FILTER (WHERE status IN ('todo','in_progress')) as pendentes FROM issues WHERE company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f' AND title LIKE 'PUBLICAR NOTICIA%' AND created_at > NOW() - INTERVAL '24 hours'; ``` - routines_fired >= 3 E pesquisas_done >= 1 E done >= 5 = OK - Qualquer zero = AVISO (pipeline com falhas) - routines_fired = 0 = CRITICO (routines nao disparam) ### Check 18: Issues blocked sem impedimento real (INC-07) Invocar tool MCP: `mcp__paperclip__diag_false_blockers` Para cada issue blocked, verificar se o assignee está em erro: ```sql SELECT a.name, a.status FROM agents a WHERE a.id = (SELECT assignee_agent_id FROM issues WHERE identifier = '{{ID}}'); ``` - Issue `blocked` + assignee `active/idle/running` + sub-tasks activas > 0 = **FALSO BLOCKER** (deveria ser `in_progress`) - Issue `blocked` + assignee `error/paused` = blocker legítimo - Issue `blocked` + sem sub-tasks + sem comentário de impedimento = suspeito, alertar Resultado: - 0 falsos blockers = OK - 1+ falsos blockers = AVISO (listar e recomendar correcção para `in_progress`) --- ### Check 19: Sessões com tokens excessivos (INC-12) Previne "Prompt is too long" e token burn massivo. Monitoriza via usage_json dos heartbeats recentes. Top agentes com maior consumo de tokens nas últimas 24h: Invocar tool MCP: `mcp__paperclip__diag_heartbeat_token_usage(hours=24)` Contagem de erros "Prompt is too long" activos (sinal imediato): Invocar tool MCP: `mcp__paperclip__diag_prompt_too_long_errors(hours=24)` - 0 erros = OK - 1+ erros = CRITICO (forçar rotação de sessão) **Forçar rotação de sessão (apaga histórico, fresh start):** ```sql DELETE FROM agent_task_sessions WHERE agent_id = '{{AGENT_ID}}' AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'; ``` --- ### Check 20: Routines presas in_progress >4h (INC-08) Issues de routine que ficam `in_progress` sem actividade bloqueiam todos os futuros disparos da mesma routine (constraint `issues_open_routine_execution_uq`). A ligação issue→routine está em `routine_runs.linked_issue_id` (não existe `issues.routine_id`). Invocar tool MCP: `mcp__paperclip__diag_stuck_routines(hours=4)` - 0 = OK - 1+ = CRITICO (routine bloqueada — cancelar a issue presa para desbloquear futuros disparos) **Desbloquear routine (cancelar issue presa):** ```sql UPDATE issues SET status = 'cancelled', updated_at = NOW() WHERE identifier = '{{IDENTIFIER}}' AND company_id = 'ebe10308-efd7-453f-86ab-13e6fe84004f'; ``` --- ### Check 21: Zombie parents — issues pai com sub-tasks todas concluídas (AP-09) Issues pai que ficam abertas indefinidamente quando todas as sub-tasks estão `done` ou `cancelled`. Poluem o dashboard e impedem distinguir trabalho parado de trabalho já feito. Invocar tool MCP: `mcp__paperclip__diag_zombie_parents` - 0 = OK - 1+ = AVISO (fechar manualmente ou alertar CEO para verificar e fechar) --- ## Formato de output ``` ## Clip Health — [data/hora] | Check | Estado | Detalhe | |-------|--------|---------| | Servico | OK/CRITICO | N processos activos / stopped | | POSIX shared memory | OK/AVISO/CRITICO | /dev/shm/PostgreSQL.* presente / em falta | | Base de dados | OK/CRITICO | Xmb, N agentes, N issues | | Heartbeats 24h | OK/AVISO/CRITICO | N falhas | | Budget | OK/AVISO/CRITICO | [agente] a X% | | Disco | OK/AVISO/CRITICO | X% livre (Xgb) | | API Health | OK/AVISO/CRITICO | status:ok / board access / timeout | | Gateway | OK/AVISO | responde / timeout | | Refs orfas | OK/CRITICO | N budget_policies orfas | | Instancias | OK/CRITICO | N processos na porta 310x | | Porta/Tunnel | OK/CRITICO | tunnel:X servidor:Y | | Permissoes bash | OK/CRITICO | N agentes sem dangerouslySkipPermissions | | Heartbeats config | OK/AVISO | N agentes sem heartbeat enabled | | Routine triggers | OK/CRITICO | N triggers com kind errado ou next_run_at NULL | | Auto-assign noticias | OK/AVISO | N issues PUBLICAR NOTICIA sem assignee | | Qualidade artigos | OK/AVISO/CRITICO | N artigos com acentos/links/titulos errados | | Cron assign-copywriter | OK/CRITICO | activo / ausente | | Pipeline noticias 24h | OK/AVISO/CRITICO | N routines, N pesquisas, N publicadas | | Falsos blockers | OK/AVISO | N issues blocked sem impedimento real | | Tokens excessivos | OK/AVISO/CRITICO | N agentes >500K tokens cached | | Routines presas | OK/CRITICO | N issues rotina paradas >4h | | Zombie parents | OK/AVISO | N issues pai com todas as subs concluídas | **Score:** N/22 OK ### Detalhes (se existirem alertas) [listar heartbeats falhados, budget alto, artigos com defeitos, pipeline parado] ### Accoes recomendadas [listar accoes concretas para resolver alertas] ``` --- ## Healing Log Registo de erros conhecidos e como evitá-los. Lido automaticamente antes de executar. ```jsonl {"date":"2026-04-07","issue":"Check 1 usava systemctl que nunca existe (Paperclip corre via pnpm dev)","fix":"Substituir por ps aux | grep paperclip + check POSIX shared memory /dev/shm/PostgreSQL.*","source":"auto"} {"date":"2026-04-07","issue":"Check 6 usava npx paperclipai doctor que não existe no projecto local","fix":"Substituir por curl localhost:3100/health — distingue: ok / board access (BD operacional) / timeout (servidor morto)","source":"auto"} ``` *Adicionar nova linha após cada erro corrigido.*