# Privia — Diagnóstico Técnico Automático (FASE 1)

> Documento gerado automaticamente pelo agente a partir da investigação do código-fonte (commit `c7572ad`).
> Data de geração: 2026-09-09. Fontes: código do repositório, schema do banco `mlabs` e execução da aplicação via container Docker.
> Confiança por item indicada entre colchetes: `DADO REAL` | `REGRA` | `INFERÊNCIA` | `CONHECIMENTO RAG`.

## 1. Resumo executivo

O **Privia** é uma plataforma web de conformidade LGPD (PHP + Yii2) dividida em módulos de negócio
(performance, risk, financial, operational, contract, task, api). O núcleo LGPD calcula **notas (0–100)**
de avaliação de contexto a partir de respostas a um questionário, com pesos por estrutura/indicador,
agregados em árvore até um "valor global" por vértice. As telas são **renderizadas no servidor**: quase todos
os números exibidos são computados em controllers/models e injetados no HTML/JS; AJAX é usado para modais,
frações e dados de detalhe. O agente a construir precisa: identificar o usuário (sessão), o tenant
(**Cliente** via `user_has_resource`/`profile.cliente_id`), a rota/registro atuais, e explicar valores seguindo a
cadeia TELA → CONTROLLER → MODEL → QUERY → TABELA → REGRA.

## 2. Stack [DADO REAL]

| Item | Valor | Evidência |
|---|---|---|
| Framework | Yii2 `2.0.16.1` | `composer.json:7` |
| PHP | `>=7.4` (runtime do container: **7.4.33**) | `composer.json:6`; `docker exec php74_apache php -v` |
| Banco | MySQL 5.7, database **`mlabs`** (host de rede `mysql57`) | `.env:4-8`; `config/db.php` |
| Sessão | `yii\web\DbSession`, tabela `session`, grava `user_id` | `config/web.php:112-127` |
| AuthState | `mdmsoft/yii2-admin` (RBAC `DbManager`), JWT (`sizeg/yii2-jwt`) p/ API | `config/web.php:23-27,100-104,150-176`; `config/params.php:18-23` |
| Frontend | Bootstrap 4 (Argon template), Highcharts, amCharts4, gauge.js, jstree, sparkline | `assets/AppAsset.php`, `assets/ArgonAsset.php` |
| Ambiente | Docker: `php74_apache` monta `C:\projetos\apps` em `/var/www/html`; `mysql57` | `docker inspect php74_apache` |
| Log | Tabelas `log` (ações do usuário) e `cm_log` (erros/warnings) | `config/web.php:67-86`; `models/Base::loga()` |

## 3. Estrutura de diretórios [DADO REAL]

```
config/          aplicação web, console, DB (mlabs + admin_ceon), params
controllers/     ~32 controllers globais (site, user, licenca, inventario*, lia*, tia*, termos-uso…)
models/          ~90 modelos globais + aq/ (Query classes)
modules/
  performance/   MOTOR LGPD: cliente, unidade-negocio, aplicacao, projeto, contexto, coleta, vertical, plano-acao…
  risk/          risco-modelo, risco-cenario, risco (probabilidade x consequência)
  financial/     fluxo caixa, DRE, BP, indicadores, endividamento
  operational/   ocorrências, criticidade (operacional)
  contract/      contratos e itens
  task/          tarefas/kanban
  api/           REST (JWT) expondo entidades de performance
views/           layouts (main, contexto, login…), views por controller, partials
assets/          bundles de JS/CSS (Highcharts, amCharts, gauge…)
web/js/          app.js (AJAX global), events.js, libs (rgraph, jstree, xlsx…)
components/      Logged, Session, ResourceManager, Formatter, Utils, JwtValidationData
rbac/            ResourceRule, OwnerRule, MethodRule (regras custom de RBAC)
commands/        console commands do Yii
```

## 4. Identidade, tenant e permissões [DADO REAL]

- **Quem pergunta:** `Yii::$app->user->id` → tabela **`user_ceon`** (PK `id`); login por **CPF** como `username`
  (`models/User.php:71-73,98,120,195-197`); convidado = `-2`/`USER_EXTERNAL` (`components/Logged.php:55`).
- **Empresa/tenant:** dois níveis:
  1. `user_ceon.organization` — texto livre (OAB), `Logged::getOrganization()` → default `'Geral'`
     (`models/User.php:24,95,122`; `components/Logged.php:107-110`).
  2. **Cliente** (escritório/empresa avaliada) — entidade real do módulo performance, ligada por
     `profile.cliente_id → cliente.id` OU por `user_has_resource` com papel
     `'C-Performance - Nível 1 - Cliente Gestor'` (`models/Profile.php:15,39;71-73`;
     `models/BaseModel.php:770-778`).
- **Super-tenant:** papéis `Ceon`/`Admin`/`CeonFinanceiro` pulam os filtros de dado
  (`components/Logged.php:131-139`); padrão em `GenericSearch::filterMy*` (`models/GenericSearch.php`).
- **RBAC:** `auth_item/auth_item_child/auth_assignment` (DbManager) + papéis por recurso na tabela
  **`user_has_resource`** (padrão `NV_*`), espelhados no authManager por `components/ResourceManager.php`.
  Checagens: `Logged::can()`, `Logged::hasModule()`, `UserHasResource::permite()`.
- **Sessão:** `session.user_id` via `writeCallback` (`config/web.php:121-127`); `Logged::afterLogin()` carrega
  scopes de contrato e painéis (`components/Logged.php:31-37`).
- **API:** JWT com claim `uid`, exige `user_ceon.can_use_api=1` (`models/User.php:173-187`;
  `modules/api/controllers/DefaultController.php`).

> **Conclusão p/ agente:** o contexto seguro vem do backend (sessão + rôle + rota), nunca de IDs livres do usuário.
> Toda consulta deve injetar isolamento de Cliente, exceto papéis Ceon/Admin.

## 5. Módulos e entidades de negócio [DADO REAL]

| Módulo | Entidades principais | Telas núcleo |
|---|---|---|
| **performance** | `Cliente → UnidadeNegocio → Aplicacao → Projeto → Contexto → Coleta → ColetaEstrutura(PVF/PVE) → ColetaIndicador → ColetaAlternativa`; `Vertical`(template), `PlanoAcao`, `ParecerTecnicoColeta` | `cliente/detail` (VALOR GLOBAL), `contexto/lgpdv2` (gauge Nível de Conformidade), `coleta/view`,`preencher`, `vertical-grupo/view` |
| **risk** | `RiscoCenario` (pontuação = "Risco Global"), `Risco` (probabilidade × consequência), `RiscoModelo*` | `risk/default/dashboard`, `risco-cenario/risco-analise-riscos`, `risco-modelo/create-risco` |
| **financial** | `cf_fc_*` (fluxo caixa), `cf_dre_*`, `cf_bp_*`, `cf_indice_*`, `cf_endividamento_*` | `financial/default/dashboard`, `indice-value/forms` |
| **operational** | `co_ocorrencia*`, `co_criticidade_*`, `co_checklist_*` | `ocorrencias/*`, `criticidade-ocorrencia/avaliacao` |
| **contract** | `cm_contract`, `cm_contract_item`, `cm_additive`, `cm_provider` | CRUD de contratos + grids de itens |
| **task** | `cm_task`, `cm_task_assignment` | kanban/calendário |
| **api** | REST controller + modelo `Cliente` | endpoints `api/default/*` (coleta com score + ícones verde/amarelo/vermelho) |

## 6. Como as telas carregam dados [DADO REAL]

- **90% server-rendered:** número → `$model->getX()` computado no controller/model e impresso no HTML ou
  serializado em `<script>` para widgets (ex.: `gauge_nivel_de_conformidade.setValueAnimated(<?= $valorGlobal ?>,2)`,
  `modules/performance/views/contexto/lgpdv2.php:3392`).
- **AJAX:** modais (`/user/meus-dados?l=modal`), notificações (`/notification/see`), frações de formulário,
  e `pesquisa-*` em `ContextoController` (estrutura, dados-absoluto/evolução, alternativa, parametro, gpp,
  plano-acao, sparkline, dados-area-preocupacao). Convenção JSON `{code,message,title,type}` (`web/js/app.js`).
- **Filtros/Autorização de tela:** `mdm\admin\components\AccessControl` (allowActions) + `Helper::checkRoute()`
  + `GenericSearch::filterMy*`.

## 7. Banco de dados — catálogo semântico inicial [DADO REAL]

Database `mlabs`. Tabelas centrais (destaque das maiores):

| Tabela | Semântica | Volume |
|---|---|---|
| `user_ceon` | usuários (CPF, `organization`, `can_use_api`) | 281 |
| `user_has_resource` | vínculo usuário→recurso com papel `NV_*` (base do tenant) | 974 |
| `profile` | perfil 1:1 (`cliente_id`) | 272 |
| `cliente`, `unidade_negocio`, `aplicacao`, `projeto`, `contexto` | hierarquia do tenant avaliado | 72/72/72/72/484 |
| `coleta` | avaliação (questionário preenchido) | 2.150 |
| `coleta_estrutura` | PVF/PVE/vertices da árvore, `pontuacao`, `taxa_substituicao`, `tipo_estrutura` | 27.962 |
| `coleta_indicador` | indicador da coleta, `pontuacao`, `taxa_substituicao` | 48.670 |
| `coleta_alternativa` | alternativa marcada (`nivel_referencia`, `peso`, `selecionado`) | 225.679 |
| `vertical*` | modelos/templates (vertical, vertical_estrutura, vertical_indicador, vertical_alternativa) | pequenas |
| `risco`, `risco_cenario`, `risco_consequencia`, `risco_vertical` | análise de risco (1,5M) | ver |
| `inventario_*` | inventário de dados pessoais (respostas 7,1M) | grande |
| `plano_aplicacao`/`pa_*` | plano de ação | ~1k |
| `log` | registro de navegação/ações (user_id, url, get, post, ip, contexto_id) | 2,4M |
| `cm_log` | erros/warnings Yii | 1,5M |
| `licenca`, `parceiro*` | contratos/licenças por cliente | 473 |
| `scope` | árvore de escopos do inventário (`cliente_id`, `parent_id`, `root`) | 11.271 |

> **Cuidado p/ agente:** tabelas com milhões de linhas (`inventario_resposta`, `risco_vertical`, `log`, `coleta_alternativa`)
> exigem LIMIT obrigatório e projeções estreitas (nunca `SELECT *`).

## 8. Motor de pontuação — cadeia completa do "Nota 79" [DADO REAL]

Cadeia descoberta (LADO A↓):

```
Tela lgpdv2 (gauge "Nível de Conformidade") ─ $valorGlobal
  ← Coleta::getValorGlobal()  (modules/performance/models/Coleta.php:280-290)
       = pontuacao do vértice (ColetaEstrutura tipo_estrutura='V', sem cod_pai)
  ← Coleta::geraPontuacao()   (Coleta.php:1072)
       indicador TIPO_LISTA: pontuacao = round(nivel_referencia da coleta_alternativa selecionada)   (Coleta.php:1115)
       estrutura folha:        round( Σ (taxa * pontuacao)/100 )                                     (Coleta.php:1170-1174)
       estrutura pai:          round( Σ (taxa_substituicao * child.pontuacao)/100 ) subindo a árvore  (Coleta.php:1201-1205)
       vértice (valor global): round( Σ (taxa_substituicao * child.pontuacao)/100 )                  (Coleta.php:1229-1240)
  ← ColetaAlternativa: nivel_referencia (0–100) + peso ; ColetaIndicador/ColetaEstrutura: taxa_substituicao
  ← answers do usuário (selecionado = 1)
Cor / rótulo:
  Contexto::getLgpdPontuacao():  >=80 verde | >=20 amarelo | senão vermelho   (Contexto.php:3189-3198)
  faixas padrão globais:         20 / 80 (Comprometedor / Mercado / Excelência)  (models/BaseModel.php:133-134)
  faixas custom por cliente/projeto: cliente_tem_faixa, projeto_tem_faixa (Coleta::getValorGlobalCor, Coleta.php:292-309)
Agregação acima: Aplicacao::atualizaDadosAplicacao() = média dos projetos; Cliente::getValorGlobal() = média das aplicações
```

Risco (paralelo): `RiscoCenario.pontuacao = round(consequencia_valor * (probabilidade/100))`
(`modules/risk/controllers/RiscoCenarioController.php:196`); limites ViaLGPD 20/50/80 → Baixo/Moderado/Alto/Crítico
(`modules/risk/models/RiscoCenario.php:36-38`; aplicado em `Contexto.php:3125-3133,3169-3177`).

## 9. Regras de negócio descobertas (inventário v1) [DADO REAL/REGRA]

| Limiar | Significado | Fonte |
|---|---|---|
| `>=80` verde / `>=20` amarelo / senão vermelho | cor da pontuação no contexto LGPD | `Contexto.php:3189-3198` |
| `20/80` | faixas padrão Comprometedor/Mercado/Excelência | `BaseModel.php:133-134` |
| `40/80`, `15/65`, `45/80`, `45/79`, `0/99` | faixas custom de clientes específicos (31, SEA 22, UNIMED, ORSEGUPS, cliente 20) | `Cliente.php:371-381`, `Coleta.php:337-356` |
| `20/50/80` | Risco Baixo/Moderado/Alto/Crítico (ViaLGPD) | `RiscoCenario.php:36-38` |
| `<=25/<=50/<=75` | escala de cor 4 níveis no gráfico de risco | `RiscoCenarioController.php:209-217` |
| operador `> / <` + Σ pontos das perguntas "Sim" | graus de criticidade operacional | `Ocorrencia.php:463-537` |
| `>=80/60/40` | faixas de uso de disco (Contexto/Infra) | `ContextoController.php:3558-3562,7104-7108` |

> Fonte original dos limiares a consolidar: construtores de faixa por cliente/projeto no banco
> (`cliente_tem_faixa`, `projeto_tem_faixa`) — o agente deve ler do banco, não hardcodar.

## 10. Observabilidade existente p/ o agente [DADO REAL]

- `log` (ações por usuário com URL/GET/POST/IP/contexto_id) — útil p/ "o que mudou" e histórico.
- `notification`/`notificacao` — avisos por usuário.
- Tabelas órfãs `bot`, `bot_acesso`, `bot_agendamento` (0 linhas, sem código) — **não** usar como base.
- Sem tabelas de memória/knowledge do agente ainda — serão criadas na camada de conhecimento.

## 11. Lacunas conhecidas (a descobertas nas próximas fases)

- Mapeamento tela→dado completo por view (FASE 3, com navegação real se disponível).
- Catálogo semântico completo do schema (FASE 4, colunas/FKs/índices).
- Regras de negócio complementares em JS e telas (FASE 5).
- Evolução temporal dos scores ("por que caiu?") via histórico de `coleta_estrutura.pontuacao` por coleta.

## 12. Arquitetura proposta do agente (mapeada ao Privia)

Decisões (ver documento de arquitetura separado):

1. **Módulo Yii2 `agent`** dentro do Privia (mesmo container PHP), chat via POST JSON em `agent/chat/*`.
2. **Contexto 100% do backend**: sessão `Yii::$app->user`, tenant via `user_has_resource`/`profile.cliente_id`,
   rota/params capturados pelo controller (widget envia só `route`+`ids`, validados no servidor).
3. **Agente com tool-calling** implementado em PHP (loop genérico, formato `tools`/`messages`), LLM plugável
   (Ollama local OpenAI-compatível por padrão; endpoint OpenAI-compatível via env).
4. **Ferramentas genéricas e read-only**: `db.query` (SELECT com LIMIT e isolamento de tenant obrigatório),
   `db.schema` (information_schema), `code.search`/`code.read` (grep restrito ao webroot), `context.get`.
   Sem ferramenta específica por tabela. READ-ONLY total na v1 (usuário dedicado sem DML).
5. **Knowledge store** no banco `mlabs`: `agent_knowledge` (fonte, tipo, entidade, rota, arquivo, regra,
   confiança), populado por command de indexação; busca por BM25/texto + FTS.
6. **Widget** de chat no layout `main.php`/`contexto.php` (padrão do drawer de notificações existente).
7. **Segurança:** usuário DB somente `SELECT`; imposição de `cliente_id` por tenant; `isCeon` como única bypass;
   limpeza de cache; rate-limit básico; prompt com guarda de "não inventar".
8. **Testes**: perguntas reais via Yii unit/codeception + script CLI de verificação de cadeia de valor.

> Regra de ouro: nenhuma resposta fabrica valor sem passar pela cadeia acima; se a origem não for encontrada,
> o agente declara a limitação.