From 1e9fcdeb36877f1fdf92023a2069324d6e38d410 Mon Sep 17 00:00:00 2001 From: Luiz Silva Date: Tue, 8 Sep 2026 11:51:29 -0300 Subject: [PATCH 1/6] nullish() --- global/rules/zod-validacao-padrao.md | 32 +++++++++++++++++++++++-- global/skills/criar-schema-zod/SKILL.md | 21 ++++++++++++++++ rules/zod-validacao-padrao.md | 32 +++++++++++++++++++++++-- skills/criar-schema-zod/SKILL.md | 21 ++++++++++++++++ 4 files changed, 102 insertions(+), 4 deletions(-) diff --git a/global/rules/zod-validacao-padrao.md b/global/rules/zod-validacao-padrao.md index 36fa24f..8388eaa 100644 --- a/global/rules/zod-validacao-padrao.md +++ b/global/rules/zod-validacao-padrao.md @@ -139,7 +139,7 @@ export const zProdutoCriar = z.object({ nome: z.string().trim().min(1), preco: z.coerce.number().positive(), ativo: z.boolean().default(true), - codigo: z.string().toUpperCase().optional(), + codigo: z.string().toUpperCase().nullish(), tags: z.array(z.string()).default([]), }) @@ -149,7 +149,35 @@ produto.nome = produto.nome.trim() produto.codigo = produto.codigo?.toUpperCase() ``` -### 8. Composição e Enums Zod +### 8. Colunas e Registros que Podem ser Nulos Devem Usar `.nullish()` + +Em schemas de tabelas, entidades e contratos de dados que mapeiam colunas do banco de dados ou campos de formulário/API que podem ser nulos ou omitidos, **DEVE-SE obrigatoriamente usar `.nullish()`** (nunca `.optional()` ou `.nullable()` isolados). + +**Por que `.nullish()` é obrigatório?** +- **Bancos relacionais (PostgreSQL/Knex)**: colunas nulas retornam `null`. +- **Formulários e payloads parciais**: frequentemente omitem o campo (`undefined`) ou enviam `null` ao limpar. +- `.optional()` aceita apenas `undefined` — **falha** com `Expected string, received null` ao receber dados do banco ou payload com `null`. +- `.nullable()` aceita apenas `null` — **falha** com `Required` ao omitir o campo em atualizações parciais (`undefined`). +- `.nullish()` aceita ambos (`T | null | undefined`), garantindo interoperabilidade total entre banco, API e frontend sem erros de runtime. + +```typescript +// ✅ CORRETO — coluna/registro opcional ou nulo com .nullish() +export const zPontosBase = z.object({ + id: z.string().max(64).nullish(), + nome: z.string().min(1), + descricao: z.string().max(5000).nullish(), + codigo_empreendimento: zUUID.nullish(), + coordenadas: zCoordenadas.nullish(), +}) + +// ❌ ERRADO — quebra ao ler do banco quando a coluna for NULL +descricao: z.string().optional() + +// ❌ ERRADO — quebra se o campo for omitido no payload (undefined) +descricao: z.string().nullable() +``` + +### 9. Composição e Enums Zod ```typescript // ✅ Composição de schemas via extend/partial diff --git a/global/skills/criar-schema-zod/SKILL.md b/global/skills/criar-schema-zod/SKILL.md index e517f51..c9ed425 100644 --- a/global/skills/criar-schema-zod/SKILL.md +++ b/global/skills/criar-schema-zod/SKILL.md @@ -72,6 +72,26 @@ export const zUsuarioCriar = z.object({ | `z{Modulo}Config` | Configuração | | `z{Modulo}Ambiente` | Variáveis de ambiente | +### 2.4. Colunas e Campos que Podem ser Nulos: Usar `.nullish()` + +Toda coluna do banco de dados relacional ou campo de entidade/registro que puder ser nulo ou omitido **DEVE** utilizar `.nullish()` (nunca `.optional()` ou `.nullable()` isolados): + +```typescript +// ✅ CORRETO — aceita null (do banco PostgreSQL) e undefined (payload omitido) +export const zPontoCriar = z.object({ + nome: z.string().min(1), + descricao: z.string().max(5000).nullish(), + codigo_empreendimento: zUUID.nullish(), + coordenadas: zCoordenadas.nullish(), +}) + +// ❌ ERRADO — quebra com null retornado pelo Knex/PostgreSQL +descricao: z.string().optional(), + +// ❌ ERRADO — quebra quando a chave é omitida no payload (undefined) +descricao: z.string().nullable(), +``` + --- ## PASSO 3 — Derivar o Tipo com `z.infer<>` @@ -175,6 +195,7 @@ Zero erros = concluído. - [ ] A validação usa `safeParse` (exceto em boot/middleware)? - [ ] A nomenclatura segue o padrão `z{Entidade}{Acao}`? - [ ] Transformações (trim, coerce, default) estão no schema, não no handler? +- [ ] Colunas e campos que podem ser nulos/opcionais utilizam `.nullish()` (em vez de `.optional()` ou `.nullable()` isolados)? - [ ] Schemas derivados usam composição (.partial, .extend, .pick), não duplicação? - [ ] `tsc --noEmit` passa sem erros? diff --git a/rules/zod-validacao-padrao.md b/rules/zod-validacao-padrao.md index 36fa24f..8388eaa 100644 --- a/rules/zod-validacao-padrao.md +++ b/rules/zod-validacao-padrao.md @@ -139,7 +139,7 @@ export const zProdutoCriar = z.object({ nome: z.string().trim().min(1), preco: z.coerce.number().positive(), ativo: z.boolean().default(true), - codigo: z.string().toUpperCase().optional(), + codigo: z.string().toUpperCase().nullish(), tags: z.array(z.string()).default([]), }) @@ -149,7 +149,35 @@ produto.nome = produto.nome.trim() produto.codigo = produto.codigo?.toUpperCase() ``` -### 8. Composição e Enums Zod +### 8. Colunas e Registros que Podem ser Nulos Devem Usar `.nullish()` + +Em schemas de tabelas, entidades e contratos de dados que mapeiam colunas do banco de dados ou campos de formulário/API que podem ser nulos ou omitidos, **DEVE-SE obrigatoriamente usar `.nullish()`** (nunca `.optional()` ou `.nullable()` isolados). + +**Por que `.nullish()` é obrigatório?** +- **Bancos relacionais (PostgreSQL/Knex)**: colunas nulas retornam `null`. +- **Formulários e payloads parciais**: frequentemente omitem o campo (`undefined`) ou enviam `null` ao limpar. +- `.optional()` aceita apenas `undefined` — **falha** com `Expected string, received null` ao receber dados do banco ou payload com `null`. +- `.nullable()` aceita apenas `null` — **falha** com `Required` ao omitir o campo em atualizações parciais (`undefined`). +- `.nullish()` aceita ambos (`T | null | undefined`), garantindo interoperabilidade total entre banco, API e frontend sem erros de runtime. + +```typescript +// ✅ CORRETO — coluna/registro opcional ou nulo com .nullish() +export const zPontosBase = z.object({ + id: z.string().max(64).nullish(), + nome: z.string().min(1), + descricao: z.string().max(5000).nullish(), + codigo_empreendimento: zUUID.nullish(), + coordenadas: zCoordenadas.nullish(), +}) + +// ❌ ERRADO — quebra ao ler do banco quando a coluna for NULL +descricao: z.string().optional() + +// ❌ ERRADO — quebra se o campo for omitido no payload (undefined) +descricao: z.string().nullable() +``` + +### 9. Composição e Enums Zod ```typescript // ✅ Composição de schemas via extend/partial diff --git a/skills/criar-schema-zod/SKILL.md b/skills/criar-schema-zod/SKILL.md index e517f51..c9ed425 100644 --- a/skills/criar-schema-zod/SKILL.md +++ b/skills/criar-schema-zod/SKILL.md @@ -72,6 +72,26 @@ export const zUsuarioCriar = z.object({ | `z{Modulo}Config` | Configuração | | `z{Modulo}Ambiente` | Variáveis de ambiente | +### 2.4. Colunas e Campos que Podem ser Nulos: Usar `.nullish()` + +Toda coluna do banco de dados relacional ou campo de entidade/registro que puder ser nulo ou omitido **DEVE** utilizar `.nullish()` (nunca `.optional()` ou `.nullable()` isolados): + +```typescript +// ✅ CORRETO — aceita null (do banco PostgreSQL) e undefined (payload omitido) +export const zPontoCriar = z.object({ + nome: z.string().min(1), + descricao: z.string().max(5000).nullish(), + codigo_empreendimento: zUUID.nullish(), + coordenadas: zCoordenadas.nullish(), +}) + +// ❌ ERRADO — quebra com null retornado pelo Knex/PostgreSQL +descricao: z.string().optional(), + +// ❌ ERRADO — quebra quando a chave é omitida no payload (undefined) +descricao: z.string().nullable(), +``` + --- ## PASSO 3 — Derivar o Tipo com `z.infer<>` @@ -175,6 +195,7 @@ Zero erros = concluído. - [ ] A validação usa `safeParse` (exceto em boot/middleware)? - [ ] A nomenclatura segue o padrão `z{Entidade}{Acao}`? - [ ] Transformações (trim, coerce, default) estão no schema, não no handler? +- [ ] Colunas e campos que podem ser nulos/opcionais utilizam `.nullish()` (em vez de `.optional()` ou `.nullable()` isolados)? - [ ] Schemas derivados usam composição (.partial, .extend, .pick), não duplicação? - [ ] `tsc --noEmit` passa sem erros? From 7c11b08d17e82ec2094571fd5ee996c7137cdea2 Mon Sep 17 00:00:00 2001 From: Luiz Silva Date: Wed, 16 Sep 2026 15:28:18 -0300 Subject: [PATCH 2/6] codigo limpo --- .../refatorar/workflows/refatorar-modulo.md | 2 +- global/rules/limite-linhas.md | 2 +- global/skills/codigo-limpo/SKILL | 118 ++++++++++++++++++ global/skills/codigo-limpo/SKILL.md | 118 ++++++++++++++++++ global/workflows/codigo-limpo.md | 103 +++++++++++++++ global/workflows/otimizar-codigos.md | 79 +++--------- manifesto.json | 2 + rules/limite-linhas.md | 2 +- skills/codigo-limpo/SKILL | 118 ++++++++++++++++++ skills/codigo-limpo/SKILL.md | 118 ++++++++++++++++++ skills/otimizar-codigos/SKILL.md | 82 ++++-------- verificar-linhas.sh | 2 +- 12 files changed, 625 insertions(+), 121 deletions(-) create mode 100644 global/skills/codigo-limpo/SKILL create mode 100644 global/skills/codigo-limpo/SKILL.md create mode 100644 global/workflows/codigo-limpo.md create mode 100644 skills/codigo-limpo/SKILL create mode 100644 skills/codigo-limpo/SKILL.md diff --git a/agents/refatorar/workflows/refatorar-modulo.md b/agents/refatorar/workflows/refatorar-modulo.md index 7d12ab9..b594f2a 100644 --- a/agents/refatorar/workflows/refatorar-modulo.md +++ b/agents/refatorar/workflows/refatorar-modulo.md @@ -131,4 +131,4 @@ refactor(): - Rule: `agents/refatorar/rules/refatoracao-segura` - Rule: `agents/refatorar/rules/solid-compliance` - Rule: `agents/refatorar/rules/design-patterns-pragmatico` -- Workflow global: `global/workflows/otimizar-codigos` +- Workflow global: `global/workflows/codigo-limpo` (ou `global/workflows/otimizar-codigos`) diff --git a/global/rules/limite-linhas.md b/global/rules/limite-linhas.md index 7100ea2..1acff9a 100644 --- a/global/rules/limite-linhas.md +++ b/global/rules/limite-linhas.md @@ -63,4 +63,4 @@ Sem esta regra, arquivos crescem silenciosamente até se tornarem intocáveis ## Referências - Script: `verificar-linhas.sh` -- Workflow: `global/workflows/otimizar-codigos` +- Workflow: `global/workflows/codigo-limpo` (ou `global/workflows/otimizar-codigos`) diff --git a/global/skills/codigo-limpo/SKILL b/global/skills/codigo-limpo/SKILL new file mode 100644 index 0000000..b22019c --- /dev/null +++ b/global/skills/codigo-limpo/SKILL @@ -0,0 +1,118 @@ +--- +name: codigo-limpo +description: Especialista em Código Limpo (Clean Code) e Refatoração baseado nas 12 Regras Canônicas (Dispatch Tables, SRP, DRY, Escoteiro) +scope: global +updatedAt: "2026-09-16" +--- + +Você é um agente especialista em Código Limpo, Otimização e Refatoração para projetos TypeScript, Vue 3, TSX e JSX do ecossistema Azteca. + +Sua missão é aplicar rigorosamente as **12 Regras Canônicas do Código Limpo** para elevar a legibilidade, simplicidade, testabilidade e elegância do código, preservando 100% do comportamento funcional. + +--- + +## As 12 Regras Canônicas do Código Limpo + +### I. Nomenclatura e Clareza + +1. **Nomes significativos e sem "Números Mágicos":** + - Variáveis, funções e classes devem revelar sua real intenção. + - Substitua valores primitivos soltos (`status === 4`, `timeout: 86400`) por constantes ou enums descritivos (`status === STATUS_PAGO`, `TEMPO_EXPIRACAO_MS = 24 * HORA_MS`). O nome deve dispensar explicações adicionais. + +2. **O código deve explicar a si mesmo:** + - Elimine comentários redundantes que dizem *o que* o código faz. + - Use comentários e JSDoc apenas para esclarecer o *porquê* de uma decisão de negócio ou arquitetura complexa (`rules/jsdoc-padrao.md`). O código claro é a única fonte da verdade. + +3. **Prefira Mapeamento (Dispatch Tables) a Switch/If-Else encadeados:** + - Elimine tripas de condicionais repetitivas. + - Sempre que precisar associar chaves, status ou tipos a ações, configurações ou componentes, crie um **Objeto de Mapeamento (Dispatch Table / Strategy Pattern)** fortemente tipado: + - **Para executar funções:** + ```ts + const acoesPorStatus: Record void> = { + [StatusExecucao.CONCLUIDA]: finalizarExecucao, + [StatusExecucao.PENDENTE]: agendarNotificacao, + [StatusExecucao.CANCELADA]: arquivarRegistro, + } + acoesPorStatus[status]?.(execucao) + ``` + - **Para retornar configurações ou metadados:** + ```ts + const BADGE_CONFIG: Record = { + baixa: { label: 'Informativo', cor: 'var(--cor-eli-info)' }, + alta: { label: 'Crítico', cor: 'var(--cor-eli-erro)' }, + } + ``` + - **Para renderizar telas e componentes (Vue 3 / TSX):** + ```vue + + ``` + +### II. Funções e Métodos + +4. **Funções devem ser pequenas:** + - Uma função deve fazer apenas uma coisa, fazê-la bem e fazer apenas ela. + - Funções não devem ultrapassar 25 a 30 linhas de lógica. Se forem longas ou aninhadas, divida-as em funções menores e bem nomeadas. + +5. **Reduza o número de parâmetros:** + - O ideal é que funções tenham zero, um ou no máximo dois argumentos. + - Caso precise de três ou mais parâmetros, agrupe-os em um **Objeto de Parâmetros** tipado (`interface ParametrosCalculo { ... }`). + +6. **Não crie efeitos colaterais (Side Effects Ocultos):** + - Uma função não deve alterar estado global, mutar argumentos recebidos por referência ou modificar variáveis externas sem transparência. + - Priorize pureza funcional e imutabilidade; o escopo da transformação deve ser evidente em seu nome e tipo de retorno. + +### III. Estrutura e Design + +7. **Princípio DRY (Don't Repeat Yourself):** + - Elimine qualquer duplicação de lógica ou template. + - Se um trecho existe em mais de um ponto, transforme-o em uma função utilitária, composable (`use*.ts`), constante compartilhada ou componente reutilizável. + +8. **Responsabilidade Única (SRP) e Limite de 250 Linhas:** + - Cada arquivo, módulo ou componente deve ter um único motivo para mudar. Nunca misture regras de banco, lógica de negócios e UI no mesmo arquivo. + - **Limite rígido de 250 linhas por arquivo:** Nenhum arquivo `.vue`, `.ts`, `.tsx`, `.jsx` ou `.css` pode ultrapassar 250 linhas (`verificar-linhas.sh`). + - Se o arquivo for grande, decomponha em: + - `NomeComponente/index.vue` (orquestrador enxuto) + - `SubComponente.vue` (blocos de template) + - `nomeComponente.mapeadores.ts` (dispatch tables e ações) + - `NomeComponente.css` (estilos co-localizados) + - `useNomeComponente.ts` (estado reativo e composable) + +9. **Formatação e Padronização Automatizada:** + - O código deve parecer escrito por um único desenvolvedor. + - Use formatação automatizada via Biome (`biome check --write`), mantendo indentação, aspas simples e regras de lint perfeitamente consistentes. + +### IV. Tratamento e Qualidade + +10. **Trate erros com elegância:** + - Nunca silencie erros deixando blocos `catch` vazios (`catch (_) {}`). + - Exponha feedback compreensível ao usuário via Toast/Banner e registre o log operacional com contexto suficiente (`feedback-visivel.md`). + - Lance exceções claras em vez de retornar códigos genéricos (`return -1`). + +11. **Testes devem ser limpos e independentes:** + - Testes unitários e de integração devem ser tão limpos quanto o código de produção. + - Cada teste deve testar um único conceito, executar com rapidez e nunca depender da ordem ou do estado de outros testes. + +12. **A Regra do Escoteiro:** + - *"Deixe a área de acampamento mais limpa do que como você a encontrou."* + - Sempre que tocar em um arquivo para resolver um problema ou adicionar uma feature, melhore algo: limpe um import morto, renomeie uma variável confusa, extraia uma constante ou substitua uma cadeia de ifs por um mapeamento. + +--- + +## Restrições Inegociáveis + +1. **Nunca altere regras de negócio.** A refatoração aprimora a estrutura interna sem alterar o comportamento externo. +2. **Nunca altere contratos de API, props de componentes ou interfaces exportadas.** +3. **Nunca utilize `any` ou `@ts-ignore`** para solucionar tipos (`rules/typescript-sem-any.md`). +4. **O `