From filament-ai-skills
Agente especializado em detectar, diagnosticar e corrigir erros no sistema. Analisa logs, browser console, exceções PHP, erros de banco de dados, erros Livewire/Filament e problemas de configuração. Ativa quando o usuário menciona: erro, exception, bug, falha, não funciona, quebrou, 500, 404, null, undefined, stack trace, log, error, crash, problema, não atualiza, não salva, tela branca.
How this skill is triggered — by the user, by Claude, or both
Slash command
/filament-ai-skills:error-detectiveThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Localizar, diagnosticar e corrigir erros no sistema de forma sistemática. Nunca pule etapas — leia os logs reais antes de sugerir correções.
Localizar, diagnosticar e corrigir erros no sistema de forma sistemática. Nunca pule etapas — leia os logs reais antes de sugerir correções.
Execute em paralelo:
# Últimas linhas do log Laravel
tail -n 100 storage/logs/laravel.log
# Logs do Horizon (se jobs envolvidos)
php artisan horizon:status
Usando as ferramentas MCP disponíveis:
browser-logs — capturar erros do console do browsertinker — executar PHP para testar código suspeitodatabase-query — verificar estado dos dados no banco| Tipo | Sintomas | Onde investigar |
|---|---|---|
| PHP Exception | Stack trace no log | storage/logs/laravel.log |
| Livewire/Filament | Erro no console, ação sem resposta | browser-logs + log Laravel |
| Banco de dados | SQLSTATE, constraint violation | Log Laravel + database-query |
| Null safe | ErrorException: Attempt to read property on null | Código + migration para verificar nullable |
| Filament path | Total não atualiza em Repeater | Ver guideline repeater-calculations.md |
| N+1 | Timeout, lentidão extrema | Telescope ou log de queries |
| Fila/Job | Dados não processados, status preso | php artisan horizon:status + log |
| Permissão | 403, acesso negado | Policy + UserRole enum |
| Frontend | CSS/JS não atualizado | npm run build ou npm run dev |
| MySQL GROUP BY | SQLSTATE[42000] only_full_group_by em Filament widget | Ver erro #9 — subquery wrapper |
Para cada erro identificado:
# Buscar por padrão de código suspeito
grep -rn "NomeDoMetodo\|NomeDaClasse" app/
# Verificar arquivo específico da stack trace
# Linha exata apontada pelo stack trace é o ponto de partida
Ferramentas:
Grep — buscar padrão no códigoRead — ler arquivo com contexto da linha do errotinker — reproduzir o erro isoladamenteNão corrija sintomas. Identifique a causa raiz antes.
Perguntas-guia:
database-query)php artisan test --compact --filter=NomeDoTestvendor/bin/pint --dirty antes de commitarCausa: afterStateUpdated do Repeater usa paths ../../campo mas o $get já está no nível root.
Solução: Ver guideline repeater-calculations.md — passar prefix: '' para o Repeater.
->afterStateUpdated(function ($get, $set): void {
self::recalculateTotals($get, $set, ''); // '' para nível root
})
Causa: Acesso a relação nullable sem ?->.
Diagnóstico:
# Verificar se FK é nullable na migration
grep -n "nullable\|->foreign" database/migrations/*_create_table.php
Solução:
// ERRADO
$record->person->name
// CORRETO (FK nullable)
$record->person?->name
Causa: O valor não foi convertido de string pt-BR (com vírgula) para float antes de formatar.
Solução:
use Illuminate\Support\Number;
// ERRADO — passa string pt-BR direto para Number::currency()
$set('total', Number::currency((float) $get('unit_price'), 'BRL'));
// CORRETO — converte para decimal primeiro
$unitPrice = (float) str_replace(['.', ','], ['', '.'], $get('unit_price'));
$set('total', Number::currency($unitPrice * $qty, 'BRL'));
Causa: Query sem escopo de tenant.
Diagnóstico:
// tinker
Tenant::find($tenantId)->orders()->count();
Order::where('tenant_id', $tenantId)->count();
Solução: Verificar se o model tem global scope ou se o resource usa ->modifyQueryUsing().
Causa: Falta de parent::setUp() em setUp(), ou ->action() não configurado.
Solução:
protected function setUp(): void
{
parent::setUp(); // OBRIGATÓRIO como primeira linha
$this->action(fn (array $data) => $this->execute($data));
}
Causa: Confundir namespaces de Form fields, Layout e Infolist.
| Tipo | Namespace correto |
|---|---|
| Form fields (TextInput, Select) | Filament\Forms\Components\ |
| Layout (Section, Grid, Tabs) | Filament\Schemas\Components\ |
| Infolist entries (TextEntry) | Filament\Infolists\Components\ |
| Actions | Filament\Actions\ |
| Schema method | Filament\Schemas\Schema |
| Get/Set utilities | Filament\Schemas\Components\Utilities\ |
Diagnóstico:
php artisan horizon:status
php artisan queue:failed
php artisan queue:failed --queue=nome_da_fila
Solução:
# Tentar re-executar
php artisan queue:retry all
# Ver detalhes do job falhado
php artisan queue:failed-table
Sintoma: CSS ou JS não reflete mudanças.
Solução: Pedir ao usuário para executar npm run build ou npm run dev.
Causa: Filament TableWidget adiciona automaticamente ORDER BY tabela.id ASC como tiebreaker de paginação. Em MySQL com only_full_group_by, esse id não está no GROUP BY nem é um agregado — query rejeitada.
Sintoma típico no erro:
SQLSTATE[42000]: ... Expression #2 of ORDER BY clause is not in GROUP BY clause
... order by total_revenue desc, profitability_items.id asc
Diagnóstico: A query usa ->groupBy(...) + ->orderByDesc(...), e Filament appenda ORDER BY model.id.
Solução: Envolver a query agrupada em uma subquery com o mesmo alias da tabela. O Filament aplica seus sorts na query externa (sem GROUP BY), tornando-a compatível com only_full_group_by:
protected function getTableQuery(): Builder
{
$subQuery = MyModel::query()
->selectRaw('
grouped_col as id, // alias id = coluna agrupada
grouped_col,
SUM(value) as total,
...
')
->where(...)
->groupBy('grouped_col', 'item_name')
->orderByDesc('total')
->limit(10);
// Outer query: Filament aplica ORDER BY mymodels.id aqui,
// que referencia o 'id' do subquery (= grouped_col) — válido
return MyModel::query()
->fromSub($subQuery, 'mymodels') // alias = nome da tabela do model
->orderByDesc('total');
}
Regras:
fromSub() deve ser exatamente o nome da tabela do model (ex: profitability_items)->orderByDesc() na query externa para que o sort do Filament seja tiebreaker, não o sort primário->limit() deve ficar no subquery (garante os top N antes do Filament reordenar)grep -n "Attempt to read property\|Call to a member function.*on null" storage/logs/laravel.log | tail -20
grep -n "select \*\|N+1\|QueryException" storage/logs/laravel.log | tail -20
grep -n "ValidationException\|validation.required\|validation.unique" storage/logs/laravel.log | tail -20
grep -n "AuthorizationException\|403\|This action is unauthorized" storage/logs/laravel.log | tail -20
Ao concluir, gerar:
## Diagnóstico: [Descrição do Erro]
### Evidências Coletadas
- Log: [linha relevante]
- Browser: [erro do console se houver]
- Banco: [estado dos dados]
### Causa Raiz
[Explicação técnica da causa]
### Correção Aplicada
- Arquivo: [path:linha]
- Mudança: [descrição]
### Verificação
- [ ] Testes executados: `php artisan test --compact --filter=...`
- [ ] Logs verificados após correção
- [ ] Pint executado: `vendor/bin/pint --dirty`
npx claudepluginhub felipearnold/filament-ai-skills --plugin filament-ai-skillsProvides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.