Cem-Tas96/claude-skills

Skills pessoais do Claude Code, sincronizados em todas as minhas máquinas (macOS + Windows). Reside em ~/.claude/skills/.

O que é claude-skills?

Este skill do Claude Code implementa um rigoroso 'Gameboy-Gate', um processo de auditoria e aprovação obrigatório para qualquer projeto que seja implantado em um servidor chamado 'Gameboy' (um Hetzner CAX11 ARM com 4 GB de RAM). O skill garante que cada novo deploy seja submetido a uma verificação completa e automatizada que inclui: cálculo de limites de recursos (memória, CPU) baseado no tipo de projeto, persistência desses limites no banco de dados do Coolify (para evitar regressões em deploys futuros), configuração de variáveis de ambiente (como NODE_OPTIONS), ativação de rate limiting no Traefik, ajuste de limites de descritores de arquivo no Docker, e um teste de carga com 50 usuários concorrentes. O resultado final é um veredito claro de 'PASS' (liberando o release) ou 'FAIL' (bloqueando o release), com diagnósticos detalhados para cada fase. Projetado para macOS e Windows, mas o servidor alvo é Ubuntu/Docker/Coolify, o skill centraliza todas as verificações e configurações necessárias para manter a estabilidade e performance do servidor, evitando que projetos mal configurados ou com vazamento de memória comprometam o ambiente compartilhado.

Funciona com✓Claude Code~Codex CLI~Cursor
npx skills add Cem-Tas96/claude-skills

Installed? Explore more Produtividade e Colaboração skills: steipete/gemini, steipete/gh-issues, steipete/skill-creator · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

🎮 Gameboy-Gate — Exame de Admissão para Cada Projeto

OBRIGATÓRIO: Todo projeto implantado no Gameboy (<GAMEBOY_IP>) deve passar por este Gate — independente de quem usa Claude, independente de qual projeto. Nenhum lançamento sem PASS.

🔒 Dados de Conexão Local: Os valores reais para <GAMEBOY_IP>, <GAMEBOY_SSH>, <COOLIFY_URL>, <WATCHDOG_*> estão no arquivo gitignored gameboy-gate/gameboy.local.md (NÃO no repositório público). Antes de cada execução do Gate, leia este arquivo e substitua os placeholders.

CONTEXTO: O Gameboy

Servidor: Hetzner CAX11 (ARM)
IP:     <GAMEBOY_IP>          # real: veja gameboy.local.md
SSH:    <GAMEBOY_SSH>         # real: veja gameboy.local.md
RAM:    4GB total
CPU:    2x vCPU (ARM Neoverse-N1 @ 2.0GHz)
Disco:  40GB SSD
SO:     Ubuntu (Docker + Coolify)
Coolify UI: <COOLIFY_URL>     # real: veja gameboy.local.md

Reserva Fixa de Overhead (NUNCA reduzir):

SO + Kernel:              400MB
Coolify (incl. DB, Redis, Proxy, Sentinel): 900MB
Buffer de Segurança:      200MB
──────────────────────────────
LIVRE para Projetos:     ~2500MB  (de 4096MB total)

Projetos já em execução (a partir de 26/06/2026 — nomes reais veja gameboy.local.md):

  • app-workflow (Activepieces): 1536MB limite / 768MB reserva
  • app-billing (Next.js): 512MB limite / 256MB reserva
  • app-landing (Estático): 256MB limite / 128MB reserva
  • Ocupado: 2304MB limite / 1152MB reserva

FLUXO DO GATE (nesta ordem, sem pular)

Fase G0: Obter Snapshot do Servidor

Sempre primeiro — esta é a verdade sobre o estado atual do servidor:

<GAMEBOY_SSH> "
free -h &&
docker stats --no-stream --format 'table {{.Name}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.CPUPerc}}' &&
df -h / &&
docker inspect \$(docker ps -q) --format '{{.Name}}: mem={{.HostConfig.Memory}}' 2>/dev/null | sort
"

Daí derivar:

  • TOTAL_RAM_MB (quase sempre 3840MB = 3.7GB como bytes)
  • COOLIFY_OVERHEAD_MB = soma de todos os contêineres do Coolify (coolify, coolify-db, coolify-redis, coolify-sentinel, coolify-proxy) atualmente
  • RUNNING_PROJECTS_MB = soma dos mem_limit de todos os contêineres de projeto em execução
  • AVAILABLE_MB = TOTAL_RAM_MB - 400 (SO) - COOLIFY_OVERHEAD_MB - RUNNING_PROJECTS_MB - 200 (segurança)

Fase G1: Cálculo de Limite (orientado por IA, sem valores fixos)

Com base no tipo de projeto, a IA calcula os limites corretos:

IDENTIFICAR TIPO DE PROJETO:
- Node.js / Next.js App       → BASE_MB = 350, WORKER_OVERHEAD = 50 por Worker
- Python / Django / FastAPI   → BASE_MB = 200, WORKER_OVERHEAD = 30 por Worker
- Go / Rust Service           → BASE_MB = 80,  WORKER_OVERHEAD = 20 por Worker
- Site Estático (nginx/caddy) → BASE_MB = 64,  WORKER_OVERHEAD = 0
- Postgres                    → BASE_MB = 128, WORKER_OVERHEAD = 10 por Conexão
- Redis                       → BASE_MB = 64,  WORKER_OVERHEAD = 0
- Worker em Background (filas) → BASE_MB = 200, WORKER_OVERHEAD = 50 por Worker
- Serviço de IA/LLM (local)   → BASE_MB = 800+ (pelo menos 2GB se modelo local)

Fórmulas de Limite:

MEM_LIMIT_MB    = min(AVAILABLE_MB * 0.8, BASE_MB * 3)   -- 80% do Disponível, máx 3x Base
MEM_RESERVATION = MEM_LIMIT_MB * 0.5                      -- 50% como mínimo garantido
MEM_SWAP_MB     = MEM_LIMIT_MB * 1.4                      -- 40% de Overhead de Swap permitido
CPU_LIMIT       = round(2.0 * (MEM_LIMIT_MB / AVAILABLE_MB), 1)  -- proporcional à parcela de RAM

NODE_OPTIONS_MB = floor(MEM_LIMIT_MB * 0.70)              -- V8 Heap = 70% do limite do contêiner
UV_THREADPOOL_SIZE = min(8, cpu_count * 2)                -- Threads libuv

Verificações de Validação ANTES de definir:

  • MEM_LIMIT_MB >= 128 (Mínimo para iniciar)
  • AVAILABLE_MB - MEM_LIMIT_MB >= 200 (Buffer de segurança permanece)
  • Soma de todos os mem_limits ≤ 3500MB (sem overcommit acima de 85% da RAM física)
  • Se a validação falhar → BLOQUEADO, informar Cem

Fase G2: Definir Limites Permanentemente no Banco de Dados do Coolify

Esta é a correção crítica contra regressões de deploy. O Coolify regenera o docker-compose.yaml a cada deploy a partir de seu próprio banco de dados. Somente se os limites estiverem no banco de dados, eles sobreviverão aos deploys.

Se o UUID do aplicativo for conhecido:

<GAMEBOY_SSH> "
docker exec coolify-db psql -U coolify -d coolify -c \"
UPDATE applications SET
  limits_memory = '<MEM_LIMIT_MB>m',
  limits_memory_swap = '<MEM_SWAP_MB>m',
  limits_memory_swappiness = 10,
  limits_memory_reservation = '<MEM_RESERVATION>m',
  limits_cpus = '<CPU_LIMIT>',
  limits_cpu_shares = 1024
WHERE uuid = '<APP_UUID>';
SELECT uuid, name, limits_memory, limits_cpus FROM applications WHERE uuid='<APP_UUID>';
\"
"

Obter UUID da URL do Coolify: <COOLIFY_URL>/project/.../application/<UUID>/...

Se UUID desconhecido → pesquisar via DB:

docker exec coolify-db psql -U coolify -d coolify -c "SELECT uuid, name FROM applications ORDER BY created_at DESC LIMIT 10;"

Fase G3: Definir Variáveis de Ambiente

No .env do projeto (via echo >> .env ou Coolify UI):

# Para Node.js/Next.js:
NODE_OPTIONS=--max-old-space-size=<NODE_OPTIONS_MB>
UV_THREADPOOL_SIZE=<UV_THREADPOOL_SIZE>

# Para Aplicações de Workflow (Activepieces etc.):
AP_MAX_CONCURRENT_JOBS=5     # previne explosão de sandbox
AP_TRIGGER_DEFAULT_POLL_INTERVAL=15  # reduz carga de linha de base do DB

# Para todas as aplicações:
# (sem logging DEBUG em produção!)
NODE_ENV=production

IMPORTANTE: Sem bug de quebra de linha! Verificar cada variável em sua própria linha:

grep -c '^[A-Z_].*=' .env  # deve ser igual ao número de variáveis
# Nenhuma linha deve conter dois '=' exceto em valores
awk -F= 'NF>2 && $1 ~ /^[A-Z_]/' .env && echo "BUG: '=' duplicado na linha!"

Fase G4: Ativar Rate Limiting do Traefik

Para CADA novo serviço público:

<GAMEBOY_SSH> "
cat > /data/coolify/proxy/dynamic/rate-limit-global.yaml << 'EOF'
http:
  middlewares:
    rate-limit-strict:
      rateLimit:
        average: 60
        burst: 120
        period: 1s
        sourceCriterion:
          ipStrategy:
            depth: 1
    rate-limit-api:
      rateLimit:
        average: 30
        burst: 60
        period: 1s
        sourceCriterion:
          ipStrategy:
            depth: 1
EOF
echo 'Configuração de rate-limit escrita'
"

O Traefik capta isso automaticamente (watch=true) — nenhum restart necessário.

Em seguida, adicionar o middleware ao roteador do aplicativo (na configuração do Traefik específica do aplicativo):

middlewares:
  - rate-limit-strict

Fase G5: Definir Limites de Descritores de Arquivo

O daemon do Docker precisa conhecer os limites. Uma vez por configuração do Gameboy, permanece persistente:

<GAMEBOY_SSH> "
# Verificar se já está definido
grep -q 'nofile' /etc/docker/daemon.json && echo 'already set' || {
  # Backup e Atualização
  cp /etc/docker/daemon.json /etc/docker/daemon.json.bak.\$(date +%Y%m%d)
  python3 -c \"
import json
with open('/etc/docker/daemon.json') as f: d = json.load(f)
d['default-ulimits'] = {'nofile': {'Name': 'nofile', 'Hard': 65536, 'Soft': 65536}}
with open('/etc/docker/daemon.json', 'w') as f: json.dump(d, f, indent=2)
print('daemon.json atualizado')
  \"
  # ATENÇÃO: systemctl restart docker reinicia todos os contêineres!
  # Somente se não houver tráfego ativo → alinhar com Cem
  echo 'NECESSITA: systemctl restart docker (pequena indisponibilidade!)'
}
"

Fase G6: Teste de Carga (o verdadeiro exame de admissão)

50 usuários concorrentes, 60 segundos:

# Teste 1: Acessibilidade Básica
curl -o /dev/null -s -w "HTTP=%{http_code} %{time_total}s\n" https://<APP_URL>/

# Teste 2: 50 requisições HTTP concorrentes (30s)
/usr/sbin/ab -n 3000 -c 50 -t 30 -r -k https://<APP_URL>/ 2>&1 | grep -E "Requests|Failed|Time per|50%|90%|99%"

# Teste 3: Monitorar Memória Durante Carga
<GAMEBOY_SSH> "for i in \$(seq 1 6); do sleep 10; docker stats --no-stream --format '{{.Name}}: {{.MemUsage}} {{.MemPerc}} CPU={{.CPUPerc}}' | grep -v NAMES; echo '---'; done" &
MONITOR_PID=\$!
/usr/sbin/ab -n 5000 -c 50 -t 55 -r https://<APP_URL>/ 2>/dev/null
wait \$MONITOR_PID

Fase G7: Critérios de PASS/FAIL

PASS (todos devem ser atendidos):

CritérioLimite
Status HTTP2xx ou 3xx (sem 502/503/504)
Latência p99< 3000ms
Crescimento de Memóriaestável (< +20% entre t=0 e t=60s)
Memória permanece abaixo do limite< 90% do limite do contêiner
Nenhuma requisição com falha< 0.1%
Servidor acessível após testeHTTP 200/302 em todas as URLs
CPU após teste< 80% sustentada

FAIL (um é suficiente para BLOQUEADO):

  • Algum contêiner está inativo/reiniciado após o teste
  • Memória atingiu o limite (> 95%)
  • Failed requests > 1%
  • p99 > 5000ms
  • Watchdog interveio durante o teste (verificar: <WATCHDOG_LOG>)

Fase G8: Resultado do Gate

═══════════════════════════════════════════════════════
🎮 RESULTADO DO GAMEBOY-GATE
═══════════════════════════════════════════════════════
Projeto:     [Nome]
App-UUID:    [UUID do Coolify]
Hora do Teste: [Data/Hora]

CÁLCULO DE LIMITE:
  RAM do Servidor:              3840MB
  Overhead do Coolify:        [n]MB (medido)
  Outros Projetos:            [n]MB (soma dos limites)
  Disponível:                 [n]MB
  Alocado:                    MEM=[n]m, CPU=[n], NODE_OPTIONS=[n]MB

CHECKS:
  ✅/❌ Limites do DB do Coolify definidos persistentemente
  ✅/❌ NODE_OPTIONS correto (sem bug de nova linha)
  ✅/❌ Rate-Limiting ativo
  ✅/❌ Limite de Descritor de Arquivo 65536
  ✅/❌ Política maxmemory do Redis definida (se Redis)
  ✅/❌ Teste de Carga 50c/30s aprovado

TESTE DE CARGA:
  Req/s:    [n]     (PASS se > 10)
  p50:      [n]ms
  p99:      [n]ms   (PASS se < 3000ms)
  Falhas:   [n]%    (PASS se < 0.1%)
  Pico de Memória: [n]MB   (PASS se < 90% do limite)

═══════════════════════════════════════════════════════
VEREDITO: ✅ PASS — LANÇAMENTO PERMITIDO
        ❌ FAIL — LANÇAMENTO BLOQUEADO ([Motivo])
═══════════════════════════════════════════════════════

MOTIVOS COMUNS DE FALHA E CORREÇÕES

FAIL: NODE_OPTIONS não definido

# Verificar:
docker exec <container> printenv NODE_OPTIONS
# Correção:
grep -n 'NODE_OPTIONS' .env  # verificar bug de nova linha
# Bug: HOST=0.0.0.0NODE_OPTIONS=... → Correção:
sed -i 's/HOST=0.0.0.0NODE_OPTIONS=/HOST=0.0.0.0\nNODE_OPTIONS=/' .env

FAIL: Contêiner com OOM após teste

Causa raiz quase sempre uma das seguintes:

  1. NODE_OPTIONS não definido → V8 aloca toda a RAM do contêiner
  2. Processos Sandbox (AP, Execução de Código) sem limite de concorrência
  3. Vazamento de memória no código do aplicativo (menos comum)
# Verificar se houve reinicialização:
docker inspect <container> --format '{{.RestartCount}}'
# Verificar se houve OOM-Kill:
dmesg | grep -i 'oom\|killed' | tail -10

FAIL: p99 > 3000ms

  1. Endpoint de API pesado sem cache → Implementar cache Redis
  2. Pool de Conexões PG esgotado → Aumentar MAX_CONCURRENT_REQUESTS ou tamanho do pool
  3. Overhead de Forward-Auth por requisição → Implementar cache JWT

FAIL: Requisições com Falha > 0.1%

  1. Timeout do Traefik menor que a resposta do aplicativo → Aumentar timeouts do Traefik
  2. Limite de FD esgotado → Passo G5 (ulimits)
  3. max_connections do PG esgotado → Definir max_connections=200 no PG

RESILIÊNCIA DE DEPLOY: O Protocolo de Sobrevivência do Deploy

Cada deploy do Coolify deve garantir o seguinte para que o servidor não quebre:

Antes de cada deploy verificar:

<GAMEBOY_SSH> "
# RAM livre deve ser > 800MB
FREE=\$(free -m | awk '/^Mem:/{print \$7}')
echo "Disponível: \${FREE}MB"
[ \$FREE -lt 800 ] && echo 'AVISO: RAM insuficiente para deploy!' || echo 'RAM OK para deploy'
# Disco deve ter > 5GB livres (pull de imagem precisa de espaço)
DISK=\$(df / | awk 'NR==2{print \$4}')
echo "Disco livre: \$((DISK/1024))MB"
[ \$DISK -lt 5242880 ] && echo 'AVISO: Disco insuficiente!' || echo 'Disco OK'
"

O que o Coolify faz durante o deploy (e o que devemos garantir):

  1. O Coolify lê seu DB → gera docker-compose.yaml com os limites definidos no DB (G2 → persistente ✅)
  2. O contêiner para → pequena indisponibilidade
  3. Novo contêiner inicia → Healthcheck aguarda até o aplicativo estar pronto
  4. O Traefik recebe novo endpoint de backend

Configuração do Healthcheck (no Coolify UI ou diretamente no DB):

docker exec coolify-db psql -U coolify -d coolify -c "
UPDATE applications SET
  health_check_enabled = true,
  health_check_path = '/api/v1/flags',  -- ou '/', '/health', '/ping'
  health_check_port = '80',
  health_check_interval = 15,
  health_check_timeout = 5,
  health_check_retries = 3,
  health_check_start_period = 90  -- 90s de tempo de inicialização para aplicações pesadas
WHERE uuid = '<APP_UUID>';
"

TABELA DE ORÇAMENTO DE MEMÓRIA (a partir de 26/06/2026)

GAMEBOY 4GB RAM — RESUMO DO ORÇAMENTO
════════════════════════════════════════════════════════════
Sistema/SO:              400MB (fixo)
Stack Coolify:          ~950MB (coolify+db+redis+sentinel+proxy)
────────────────────────────────────────────────────────────
DISPONÍVEL PARA PROJETOS: ~2650MB
════════════════════════════════════════════════════════════
app-workflow:          1536MB limite / 768MB reserva
app-billing:            512MB limite / 256MB reserva
app-landing:            256MB limite / 128MB reserva
────────────────────────────────────────────────────────────
AINDA LIVRE:           ~346MB de margem de limite
════════════════════════════════════════════════════════════

AVISO: O Gameboy está cerca de 91% de sua capacidade de limite.
Novo projeto > 300MB precisa de rebalanceamento (reduzir
projeto existente ou otimizar stack Coolify).

Se um novo projeto precisar de mais de 200MB → primeiro verificar se o landing (256MB) pode ser reduzido para 128MB (site estático, precisa de pouca RAM).


CHECKLIST PARA CADA DEPLOY (Referência Rápida)

ANTES DO DEPLOY:
  [ ] RAM livre > 800MB
  [ ] Disco > 5GB livre
  [ ] Limites do DB do Coolify definidos (não 0!)
  [ ] NODE_OPTIONS no .env correto (sem bugs de quebra de linha)
  [ ] Rate-Limiting configurado para endpoints públicos

APÓS DEPLOY:
  [ ] Contêiner em execução (docker ps → Up X seconds (healthy))
  [ ] URL acessível (HTTP 2xx/3xx)
  [ ] NODE_OPTIONS ativo (docker exec <c> printenv NODE_OPTIONS)
  [ ] Memória estável (docker stats → não no limite)
  [ ] Sem OOM-Kill (dmesg | grep -i 'oom')
  [ ] CPU sustentada < 80%

NOTAS ADICIONAIS

  • Este skill é parte do conjunto pessoal de skills do Claude Code de Cem-Tas96, mantido em ~/.claude/skills/.
  • O arquivo gameboy.local.md contém dados sensíveis de conexão e não deve ser versionado.
  • Qualquer modificação nas fases do Gate deve ser aprovada por Cem.

Habilidades Relacionadas