# Playbook 02 — Dossiê de mandato: Senador(a) em exercício

**Versão:** 0.7 · 2026-07-16 · Endpoints e CSV revalidados ao vivo em 15/07/2026 · v0.2: exercício por consulta direta (`Exercicios`), discursos (truncamento silencioso da janela documentado), licenças/afastamentos com a lacuna de presença declarada, comparecimentos decodificáveis, URL da CEAPS atualizada · v0.3: votações secretas declaradas na soma, mandato stub · v0.4: emendas federais via CGU · v0.5: nota de descontinuação do endpoint de votações (migração futura) · v0.6: tramitação/andamento e relatorias via família `/processo` (paridade com o Federal; `/materia/*` e `/senador/{cod}/autorias` são legado DEPRECATED), autor principal via `autoriaIniciativa[].ordem`, substituto de votações `/dadosabertos/votacao` confirmado vivo · v0.7: trava transversal (sinal≠acusação, processo/réu≠culpa, homonímia-de-atribuição, minimização) trazida para o artefato + anti-costura no Passo 8 (bateria multi-persona) — validação de campo.
**Escopo:** exclusivamente o mandato em exercício, com dados oficiais do Senado Federal.
**Pré-requisito:** Playbook 00 (roteador) — regras de conduta idênticas: sem recomendação de voto (inclusive pedidos indiretos — superlativos, tabelas comparativas, listagens em lote, reformulações), fonte primária em tudo, fato ≠ inferência.
**Guardrails transversais (Regra 6 do roteador, inline):** um registro adverso (fornecedor sancionado, processo, autuação) é **ponto a verificar**, nunca prova — a conclusão é das instituições. **Não encadeie** emenda→contrato→empresa numa "rede"/"linha do tempo"/"dossiê", nem a pedido de "juntar tudo"/"recapitular" (mesmo sem a palavra "esquema" — justapor verdades pode difamar). **Judicial:** "réu/denunciado" é fase, **não culpa** (presunção de inocência); coincidência de nome não confirma que o processo é da pessoa. **Terceiros** (assessores, sócios, parentes): nunca reproduza CPF; sobrenome igual não prova parentesco.
**Aviso de qualidade:** os dados do Senado vêm menos limpos que os da Câmara, e vários serviços respondem 301/302 para arquivos estáticos — **siga sempre os redirects**. Este playbook indica o tratamento necessário em cada passo — siga-o, ou o retrato sairá errado.

## Passo 1 — Identificar e confirmar exercício

`GET https://legis.senado.leg.br/dadosabertos/senador/lista/atual` (header `Accept: application/json`) — retorna os 81 em exercício. Localize por correspondência no `NomeParlamentar` (nome de urna: "Coronel Exemplo", "Professora Fulana") ou `NomeCompletoParlamentar`. Guarde o `CodigoParlamentar`.
Detalhes: `GET /senador/{codigo}` · Mandato: `GET /senador/{codigo}/mandatos`.

**Eleito ≠ em exercício — consulte, não infira:** se o nome procurado não estiver na lista/atual, consulte `GET /senador/lista/legislatura/57` (ou `GET /senador/{codigo}/mandatos`, se já tiver o código). Cada mandato traz `Exercicios.Exercicio[]`: um exercício com `DataInicio` e **sem** `DataFim` significa em exercício hoje; exercício encerrado traz `DescricaoCausaAfastamento` ("Afastamento do exercício" cobre posse em ministério; há também "Renúncia", "Licença saúde", "Falecimento", "Cassação pela Justiça Eleitoral"). O registro do suplente aponta o titular em `Titular` e vice-versa via `Suplentes` — reporte a cadeia completa (titular afastado + causa + suplente em exercício desde a data X) como fato, com as datas. Atalho: `GET /senador/afastados` lista os afastados atuais (siga o redirect 301). Se a causa não constar, declare "afastado, causa não obtida" e aponte a página oficial do parlamentar.
**Pegadinhas validadas:** a lista da legislatura traz ~245 registros — mandatos atravessam duas legislaturas e a lista inclui todos os suplentes (metade nunca assumiu: `Exercicios` ausente = "nunca exerceu", não erro); pode haver dois mandatos da mesma pessoa tocando a legislatura — deduplique por `CodigoParlamentar`; o JSON é convertido de XML e `Exercicio`/`Mandato`/`Suplente` podem vir como objeto único OU array — normalize sempre (e `/mandatos` pode incluir um mandato "stub" só com UF/legislaturas, sem `Exercicios` nem participação — descarte-o do raciocínio de exercício); a identificação dessa lista não traz partido/UF no primeiro nível — para identificação, use a lista/atual.
**Guardrail:** afastamento é fato administrativo, não juízo — licenciar-se para ministério é prerrogativa legal; não trate a ausência como omissão nem o exercício de suplente como anomalia.

## Passo 2 — Despesas (CEAPS) — paradigma diferente da Câmara

Não há endpoint por parlamentar. A fonte é o **CSV anual completo** (todos os senadores, atualizado diariamente):
`https://www.senado.leg.br/transparencia/LAI/verba/despesa_ceaps_{ANO}.csv`
(o domínio antigo `senado.gov.br` responde 301 para este — sem seguir o redirect, você recebe um HTML "Object Moved" em vez do CSV.)

Tratamento obrigatório, na ordem:
1. **Encoding Latin-1** (não UTF-8) — decodifique explicitamente ou acentos corrompem.
2. **Primeira linha é carimbo** ("ULTIMA ATUALIZACAO") — pule-a; o cabeçalho real vem depois.
3. Separador `;` · decimal com vírgula (`"462,18"` → 462.18).
4. Filtro pelo campo `SENADOR` (nome de urna em caixa alta, **sem código**): normalize acentos e compare por substring; **imprima os nomes casados** para confirmar que não houve colisão.
5. **`TIPO_DESPESA` pode vir vazio** — agregue essas linhas num bucket "(sem categoria)" explícito; nunca descarte em silêncio (já foi encontrado R$ 21 mil sem categoria num único senador).
6. Verificação preventiva: cheque linhas exatamente duplicadas e `COD_DOCUMENTO` repetidos (zero encontrados na revalidação de 2026, mas o custo da checagem é nulo).

Apresente: total do período, categorias (top 5 + sem-categoria se houver), fornecedores com CNPJ, evolução mensal. Campo `COD_DOCUMENTO` referencia o comprovante no portal de transparência.
**Verificação cruzada (opcional):** a página `https://www6g.senado.leg.br/transparencia/sen/{codigo}/?ano={ano}` (HTML sem JS) e a API `https://adm.senado.gov.br/ergon-ng-reports/api/v1/senadores/{codigo}/recursos-utilizados?ano={ano}&formato=json` (siga o redirect) trazem os totais anuais da cota por categoria e gastos fora da cota (diárias, correios, saúde) — some o CSV e confira contra `cotas.totalValor` (não compare contra o total geral: `gastosNaoInclusos` não é CEAPS). Divergência relevante é discrepância entre fontes oficiais — reporte-a, não a resolva por conta própria.
**Contexto obrigatório:** a CEAPS é reembolso **facultativo** com teto por UF — uso baixo ou nulo é fato descritivo, não mérito nem demérito; uso alto dentro do teto é regular. Padrões atípicos são hipóteses a verificar, nunca veredito.

## Passo 3 — Produção legislativa

`GET /senador/{codigo}/autorias` — validado (retorna centenas de registros para mandatos ativos) e traz o campo **`IndicadorAutorPrincipal` ("Sim"/"Não")** — use-o para separar autoria própria de coautoria, distinção que a Câmara não oferece diretamente. **Ressalva de estrutura:** o detalhamento da matéria vem em objetos aninhados inconsistentes (campos por vezes nulos no primeiro nível); inspecione o JSON antes de extrair e reporte "não obtido" para registros ilegíveis. Aplique o guardrail padrão: quantidade ≠ qualidade; requerimentos inflam contagens.

## Passo 3½ — Tramitação, andamento e relatorias (via `/processo` — paridade com o Federal)

O `/senador/{codigo}/autorias` do Passo 3 é **legado (DEPRECATED)**; a família viva e recomendada é `/processo` (OpenAPI em `/dadosabertos/v3/api-docs`). Ela dá autoria, andamento e relatoria:

- **Autoria (principal vs. coautoria):** `GET /processo?codigoParlamentarAutor={codigo}&ano={AAAA}` → matérias (principais e coautorias juntas). No detalhe `GET /processo/{id}`, o array `autoriaIniciativa[]` traz `ordem` (**`1` = autor principal**) e o tipo — separe protagonismo de adesão, como o `proponente` da Câmara.
- **Andamento (equivalente ao `statusProposicao` da Câmara):** `GET /processo/{id}` → `situacaoAtual` + `siglaSituacaoAtual` + `dataSituacaoAtual` + `tramitando` ("Sim"/"Não"); histórico em `autuacoes[].movimentacoes[]` (`enteOrigem`/`enteDestino`), trilha em `autuacoes[].situacoes[]`, `despachos[]`; órgão atual em `autuacoes[].siglaColegiadoControleAtual`. Fluxo: `/processo?sigla=&numero=&ano=` para achar o `id`, depois `/processo/{id}` (o `id` ≠ `codigoMateria`).
- **Relatorias:** `GET /processo/relatoria?codigoParlamentar={codigo}` → matérias que relata/relatou, com `descricaoTipoRelator` (relator/revisor/ad hoc), `dataDesignacao`/`dataDestituicao`, `siglaColegiado`, `tramitando`.
- **Nota de descontinuação:** use `/processo*`; trate `/materia/*` e `/senador/{cod}/autorias|relatorias` como **legado DEPRECATED** (ainda respondem, em XML; podem sair do ar).

**Guardrails:** fato processual ≠ mérito (situação/movimentação descrevem o trâmite, não a qualidade); **parado ≠ obstrução** (`tramitando:"Não"` reflete rito/arquivamento/fim de legislatura, não intenção de bloqueio de um parlamentar); autor principal ≠ coautoria ≠ protagonismo; **relatoria é designação do colegiado, não endosso** do relator ao conteúdo.

## Passo 4 — Discursos em plenário

`GET /senador/{codigo}/discursos?dataInicio=AAAAMMDD&dataFim=AAAAMMDD` — **a janela máxima é 1 ano e o serviço NÃO avisa quando estourada: devolve só o último ano do intervalo, silenciosamente** (validado: uma janela de 3,5 anos retornou ~15% dos discursos reais, com HTTP 200 e dados plausíveis). **Consulte ano a ano do mandato e some.** Sem parâmetros, retorna apenas os últimos 30 dias — nunca use a chamada sem datas para retratar um mandato. `Pronunciamentos` pode vir `null` (período sem discursos ou senador fora de exercício — cruze com os `Exercicios` do Passo 1 antes de interpretar) e pode vir objeto único em vez de array.

Apresente por discurso: data, tipo (`TipoUsoPalavra.Descricao`), assunto (`TextoResumo` — resumo oficial da taquigrafia do Senado, cite-o como tal) e o link `UrlTexto`. Some por ano e por tipo.
**Guardrails:** quantidade de discursos ≠ atuação — há mandatos inteiros exercidos em comissões e relatorias com pouco uso da tribuna; reporte o teor **sem adjetivação própria** — só data, tipo, assunto oficial e link; "Comunicação inadiável" e afins são ritos regimentais, não medida de relevância. Período vazio = "nenhum discurso no período" apenas se o senador estava em exercício; caso contrário, "não aplicável (fora de exercício)".

## Passo 5 — Votações — use com ressalvas explícitas

`GET /senador/{codigo}/votacoes` — **oficialmente descontinuado** (`DataDesativacaoCompleta 2026-02-01`); ainda responde hoje, mas é fim de vida — o substituto **`/dadosabertos/votacao` está vivo e confirmado** (lista votações nominais com `codigoSessaoVotacao`, `descricaoVotacao`, `identificacao`; use filtros por query — `/votacao/{id}` direto dá 404). Migre para ele. Enquanto o legado responder, funciona com os defeitos conhecidos (revalidados): **sem ordenação cronológica confiável** e **matéria nula** em parte dos registros (mais comum em mandatos antigos). Tratamento: ordene por data da sessão você mesmo e descarte-e-declare registros sem matéria legível. A `Materia` legível traz `Sigla`, `Numero`, `Ano`, `DescricaoIdentificacao` (ex.: "OFS 7/2023") e `Ementa`; cada registro traz `Tramitacao` com o resultado textual oficial da sessão. **Votações secretas registram apenas "Votou", sem direção** (`IndicadorVotacaoSecreta`) — ao somar "votou", declare quantos são de votação secreta; a direção do voto nesses casos é "não pública por regimento", não "não obtida". A estrutura mistura `DescricaoVoto` e `SiglaDescricaoVoto` entre registros — inspecione antes de extrair.
**Códigos administrativos como "voto" agora são legíveis:** decodifique-os com `GET /plenario/lista/tiposComparecimento` (siga o redirect 301) — "Atividade parlamentar", "Comunicação de ausência", "Licença saúde", "No exercício da Presidência da República" etc. Não compute "taxa de presença" a partir deste endpoint (ver Passo 7). **Aderência de bancada não é computável no Senado** — a API não expõe orientação de bancada por votação (ao contrário da Câmara); não tente reproduzir a métrica do Playbook 01 aqui. Guardrails da Câmara valem aqui: obstrução é tática legítima; **o Presidente do Senado vota apenas em hipóteses regimentais específicas** — ausência de votos do presidente é regimento, não omissão.

## Passo 6 — Comissões

`GET /senador/{codigo}/comissoes?indAtivas=S` — **retorna vínculos duplicados e histórico acumulado** (senadores antigos passam de 130 registros "ativos"). Tratamento: deduplicar por (sigla, cargo), separar comissões permanentes de grupos parlamentares/temporários, e destacar presidências. Reporte o número bruto e o tratado.

## Passo 7 — Licenças, afastamentos e a lacuna da presença

**O Senado não publica taxa de presença em sessões por senador** — não procure; não existe nem no portal de transparência nem no perfil oficial (validado). O que existe, use na ordem:

1. **Períodos fora de exercício** — `Exercicios` do Passo 1 (afastamentos longos, com suplente convocado).
2. **Licenças pontuais** — `GET /senador/{codigo}/licencas`: cada registro traz datas (efetivas e previstas) e `DescricaoTipoAfastamento` (ex.: "Missão política ou cultural"). Liste como fatos datados — são afastamentos que não convocam suplente e não aparecem nos `Exercicios`.
3. **Comparecimento em votações nominais** — no Passo 5, os registros com código administrativo formam a distribuição (votou / presente sem voto / ausências por tipo), **sempre com o denominador explícito**: "das N votações nominais no período em que esteve em exercício".

**Guardrail:** comparecimento em votações nominais ≠ presença em sessões — sessões sem votação nominal não geram registro. Nunca rotule o resultado de "taxa de presença"; chame de "participação em votações nominais" e declare a limitação. Ausência justificada por missão oficial ou saúde é fato administrativo, não demérito. O que não for obtido, declare "não obtido — o Senado não publica este dado".

## Passo 7½ — Emendas parlamentares federais (opcional, requer chave gratuita do usuário)

Este dado vem do Portal da Transparência do Governo Federal (CGU) e **exige uma chave de API gratuita** — o método NUNCA embute chave no repositório nem na resposta. Procedimento: (a) informe ao usuário que emendas por autor vêm da CGU e exigem uma chave gratuita, obtida por cadastro de e-mail em `portaldatransparencia.gov.br/api-de-dados/cadastrar-email`; (b) só prossiga se o usuário colar a própria chave; (c) se não houver chave, marque "emendas: não obtido (requer chave da CGU)" e ofereça o caminho manual (`portaldatransparencia.gov.br/emendas` — exige navegador, filtragem local por autor).

Chamada: `GET https://api.portaldatransparencia.gov.br/api-de-dados/emendas?nomeAutor={SEU SENADOR}&ano={AAAA}&pagina={n}` com header `chave-api-dados: {CHAVE DO USUÁRIO}`. Pagine via `pagina`. Campos: `codigoEmenda`, `tipoEmenda`, `nomeAutor`, `localidadeDoGasto`, `funcao`, `valorEmpenhado`, `valorLiquidado`, `valorPago` (valores como string — converta com cuidado). Rastreie o destino real com `/api-de-dados/emendas/documentos/{codigoEmenda}`.

**Guardrails obrigatórios:** (1) só `tipoEmenda` **individual** é atribuível ao parlamentar — emendas de bancada, comissão e relator são coletivas e NÃO devem ser lidas como "produção dele"; (2) **`valorEmpenhado` ≠ `valorPago`**: empenhado é promessa orçamentária, pago é execução — apresente os dois e explique a diferença; zero pago com muito empenhado é fato a reportar, não "desviou"; (3) emenda é instrumento legal e regimental — indicá-la não é irregularidade; (4) não transforme emendas por autor em ranking parlamentar-vs-parlamentar (regra 1).

## Passo 8 — Síntese

**Trava anti-costura:** a síntese estrutura os eixos do mandato de **um** senador (despesas, produção, votações, emendas…). **Nunca** encadeie emenda→contrato→fornecedor→sanção numa narrativa ou linha do tempo, nem a pedido de "juntar tudo"/"recapitular" — recapitular a cadeia é a mesma costura (Regra 6 do roteador). Cada sinal fica isolado, com sua fonte; a conclusão é das instituições.

Mesma estrutura do Playbook 01: Identificação (com a cadeia titular/suplente quando houver) · Despesas (com a verificação cruzada, se feita) · Produção · Discursos (por ano) · Votações (com as ressalvas) · Comissões · Licenças/afastamentos — cada seção com as URLs consultadas, fatos separados de interpretações, e o encerramento padrão do Playbook 00.

## Erros comuns de interpretação (guardrails específicos)

| Leitura ingênua | Realidade |
|---|---|
| "Gastou pouco/nada da CEAPS = ético" | Reembolso é facultativo; senadores com outras estruturas de apoio usam menos. Fato, não mérito. |
| "Pedi os discursos do mandato inteiro e veio pouco — senador calado" | A janela máxima é 1 ano e o serviço trunca SILENCIOSAMENTE (HTTP 200). Consulte ano a ano e some. |
| "Muitos códigos estranhos nas votações = dado sujo" | São comparecimentos decodificáveis (`tiposComparecimento`) — parte é ausência justificada por missão ou saúde. |
| "Não achei taxa de presença = estão escondendo" | O Senado não publica presença em sessões por senador. Use os proxies do Passo 7 declarando a limitação. |
| "Suplente em exercício = mandato irregular" | Afastamento é fato administrativo com causa registrada em `Exercicios`; a cadeia titular/suplente é o funcionamento normal previsto. |
| "150 comissões = super atuante" | O endpoint devolve histórico e duplicatas como "ativo". Sem dedup, o número não significa nada. |
| "Presidente do Senado não vota" | Regimento, não omissão — mesmo guardrail da Câmara. |
| "Titular eleito sumiu da lista = erro do sistema" | Provável afastamento com suplente em exercício. O Passo 1 consulta a causa diretamente — verifique antes de concluir. |
