# AGENTS.md — Felicidade Imobiliária (2.0)

Guia de trabalho para o Codex neste repositório. Leia também `DESAFIOS.md` no início de toda sessão.

## O que é este projeto

Migração do sistema legado **`felicidade_catia`** (CodeIgniter 3, gestão imobiliária/locação) para **Laravel 12 / PHP 8.2**, feita **módulo a módulo**. O sistema é **single-tenant** (uso interno de uma imobiliária) — não há isolamento multi-empresa como no crm_impactus.

**Domínio**: locadores, locatários, fiadores, imóveis, contratos de locação, boletos (CNAB remessa/retorno), contas a pagar/receber, movimentos financeiros, repasses, proponentes/propostas, corretores, procuradores, usuários e relatórios (DIMOB, honorários, inadimplentes).

**Stack**: Laravel 12, Blade (sem SPA), MySQL, Tailwind CSS v4 via Vite. PHPUnit puro para testes.

### O projeto legado é APENAS referência de leitura

`C:\GitHub\felicidade_catia` (CodeIgniter) **nunca deve ser modificado** — serve só como base para entender as regras de negócio. Todo código novo vai em `C:\GitHub\felicidade_catia2.0`.

## Decisões de arquitetura da migração (ler antes de mexer em qualquer módulo)

- **Banco compartilhado, schema legado.** O Laravel aponta para o MESMO banco MySQL de produção (`felicidade_imobiliando`), coexistindo com o CodeIgniter durante a migração. Os Models Eloquent mapeiam os nomes legados: tabela `tb_usuario`, PK `id_usuario`, coluna de senha `senha`, sem timestamps do Eloquent. Ver `app/Models/User.php` como referência do padrão (`$table`, `$primaryKey`, `public $timestamps = false`, `getAuthPasswordName()`).
- **Colunas reais são MAIÚSCULAS** (`ID_USUARIO`, `LOGIN`, `SENHA`...), mas todo Model usa nomes minúsculos. Isso só funciona porque `config/database.php` seta `PDO::ATTR_CASE => PDO::CASE_LOWER` nas conexões mysql/mariadb — **nunca remova essa opção**, sem ela todo `$model->coluna` vem `null` contra o banco real (o SQLite local não pega esse bug porque as migrations-espelho já são minúsculas). Ver `DESAFIOS.md`.
- **Migrations são "espelhos" das tabelas legadas, guardadas com `Schema::hasTable()`.** Não possuímos o schema — ele já existe em produção. As migrations em `database/migrations/*_mirror_*.php` recriam um SUBCONJUNTO das colunas (só as que o módulo usa) apenas para montar o banco local e o de testes (`RefreshDatabase` no sqlite `:memory:`). Elas não fazem nada quando a tabela já existe (produção), e o `down()` nunca dropa tabela legada. Ao migrar um módulo novo, estenda o espelho da tabela com as colunas que aquele módulo passa a usar.
- **Senhas legadas são bcrypt `$2a$` (phpass com CRYPT_BLOWFISH)** — compatíveis com `Hash::check` do Laravel, MAS o Laravel 12 por padrão só aceita hash `$2y$` e lança `RuntimeException` pra `$2a$`/`$2b$`. Precisa de `HASH_VERIFY=false` no `.env` (já setado). Não re-hashar; o login existente funciona sem migração de dados.
- **Campos de texto herdados: legado força maiúsculas via JS global.** Em CI3, qualquer `input[type=text]` vira maiúsculo ao digitar (exceto campos de e-mail, que viram minúsculo) — ver `assets/js/util.js`/`inicio.js` no legado e a entrada correspondente em `DESAFIOS.md`. Ao migrar uma tela nova, replique esse comportamento no campo equivalente (JS no client **e** normalização no `FormRequest` do servidor, pra não depender só de JS) — a menos que o campo seja claramente algo que não deveria maiusculizar (senha, e-mail).
- **Trilha de auditoria única em `tb_log`** (compartilhada com o CI). Use `App\Models\ActivityLog::registrar($idUsuario, $descricao, $ip, $idFuncionalidade)`. Colunas: `id_log`, `dt_log`, `id_usuario`, `id_funcionalidade`, `ip`, `descricao`.
- **Situações do usuário** (`tb_usuario.id_status`): 1=ativo, 2=inativo, 3=bloqueado, 4=excluído (lógico). Constantes em `User::STATUS_*`.

## Controle de qualidade obrigatório (dois gates, herdados do crm_impactus)

Toda tarefa que cria/altera código em `app/` tem dois gates antes de ser considerada concluída. Nenhum é opcional, nenhum espera o usuário pedir. Não se aplica a tarefas que não mexem em código. As skills de processo citadas abaixo vivem em `.Codex/skills/` (portadas do crm_impactus e adaptadas a este projeto single-tenant) — **use-as de verdade, não só cite o nome**.

### Gate 1 — Testes (PHPUnit puro)

- O framework é **PHPUnit puro** (classes estendendo `Tests\TestCase`, métodos `test_*`), **não Pest**. Siga a convenção de `tests/Feature` e `tests/Unit`. Skill de referência: `.Codex/skills/unit-tests`.
- O comportamento novo/alterado precisa estar coberto por teste.
- **Todo endpoint que recebe um ID de recurso ou credencial exige teste de autorização**: aqui (single-tenant) isso é principalmente autenticação + situação do usuário (rota protegida por `auth`, usuário inativo/bloqueado barrado, visitante redirecionado ao login). Ver `tests/Feature/Auth/LoginTest.php` como referência.
- Comportamento que o PHPUnit não consegue exercitar de verdade (ex. CSRF/419 real — o Laravel desliga a verificação em ambiente de teste) é validado manualmente via QA documentada — skill `.Codex/skills/qa-procedures`, não um teste automatizado forçado.
- Rode `php artisan test` e confirme que os testes novos passam e nada quebrou. Para avaliar cobertura de um módulo específico, skill `.Codex/skills/test-coverage`.
- **Ao incluir algo novo numa página que já tem JS/comportamento existente (script inline, outro componente Alpine, toggle de seção), teste o fluxo INTEIRO da página depois — não só a funcionalidade nova isolada.** PHPUnit não executa JavaScript, então uma mudança de Blade pode quebrar silenciosamente um script que dependia de um `id`/estrutura que essa mudança removeu, sem nenhum teste automatizado acusar (o teste da feature nova passa, o resto da página quebra). Caso real (2026-08-22, popup de CRUD de lookup): converter os `<select>` de Estado Civil/Gênero/etc. pro partial `select-com-lookup.blade.php` tirou o atributo `id` que o script de mostrar/esconder seções de `_form.blade.php` (PF/PJ, cônjuge, conta bancária/PIX) já usava via `getElementById` — o erro síncrono resultante parou o script inteiro, e cônjuge/conta bancária ficaram sempre visíveis independente da seleção. Antes de reportar uma tarefa como concluída, quando ela mexeu numa página com lógica cliente pré-existente: releia o HTML renderizado (ou rode via navegador) e confira que os comportamentos que JÁ estavam lá continuam funcionando, não só o que foi adicionado.

### Gate 2 — Revisão de segurança (secure-coding)

- Aplique as regras da skill `.Codex/skills/secure-coding` (OWASP Top 10 2025, adaptada a este projeto) ao escrever o código; rode o checklist sobre o diff antes de reportar.
- Ao reportar, cite o que foi verificado e classifique achados: 🔴 Crítico / 🟡 Importante / 🔵 Melhoria.
- Corrija na mesma sessão todo achado sem trade-off de produto (bug, defesa ausente, config do repo). Leve ao usuário só o que muda a experiência ou exige acesso que você não tem. 🔴 nunca espera.

## Convenções

- **Commit e push são MANUAIS, feitos pelo usuário.** Nunca crie commit nem faça `git push` automaticamente — nem ao final de uma tarefa, nem ao publicar. Implemente, teste, deixe as mudanças prontas na árvore de trabalho e informe o comando; quem commita/pusha é o usuário (mesma regra do crm_impactus).
- **Design**: interface nova, limpa e moderna (Tailwind). **Não** replicar o AdminLTE do legado.
- **Exclusões** exigem confirmação client-side (`confirm()`), como no crm_impactus, além da verificação de autorização no servidor.
- **Tarefas multi-etapa**: documente em `docs/tasks/<slug>.md` (objetivo, decisões, progresso, próximos passos) para sobreviver a compactação de contexto.
- Texto voltado ao usuário em português.
- **Layout**: toda tela autenticada estende `layouts.authenticated` (sidebar esquerda fixa + header com título da página), nunca `layouts.app` diretamente — `layouts.app` é só o shell HTML base, usado por `layouts.authenticated` e pela tela de login. Ver `resources/views/partials/sidebar.blade.php` e `docs/tasks/sidebar-e-layout.md`. Conteúdo interno usa `@section('content')` (não `@section('body')`, que pertence ao `layouts.app`) e não deve limitar a largura de forma apertada (`max-w-4xl` era estreito demais — index/listagem não usa cap de largura, formulário usa `max-w-6xl`).

## Protocolo de decisões técnicas

Ao tomar decisão de stack/biblioteca/padrão/segurança: apresente opções com prós/contras e uma recomendação justificada (DRY, reaproveitar padrões já existentes). Não decida silenciosamente.
