Pular para o conteúdo

Apêndice B — Módulos Concretos de Implementação


O Apêndice Técnico “O Formigueiro” define a arquitetura: orientação a eventos, núcleo (Event Bus + Registry), colônias autônomas, regras duras de isolamento e fases de evolução. Ele responde ao o quê.

Este apêndice responde ao como. Para cada colônia, abre uma ficha técnica: o que faz, onde termina sua responsabilidade, quais eventos consome e produz, quais dados possui, que lógica executa e quais técnicas ou algoritmos são candidatos de implementação. Tecnologias são sempre referências — não decisões fechadas.

A estrutura segue as três fases do Formigueiro, mantendo a mesma espinha dorsal. Módulos mais complexos são quebrados em sub-colônias especializadas. O critério de quebra é simples: quando a lógica interna for distinta o suficiente para ter estado próprio, ciclo de vida independente e fronteira clara com os demais.

O objetivo deste documento é servir de base para a decisão de escopo do MVP antes de qualquer linha de código.


Cada colônia usa o seguinte template:

  • Contexto e responsabilidade — o que faz e, explicitamente, o que não faz.
  • Eventos consumidos — o que a colônia escuta no barramento.
  • Eventos produzidos — o que a colônia publica após processar.
  • Estado próprio (write model) — as entidades que a colônia possui e ninguém mais escreve.
  • Lógica e algoritmos — o processamento interno, com detalhamento dos passos e pseudocódigo onde ajudar.
  • Técnicas e referências de implementação — stacks, algoritmos e padrões candidatos.
  • Sub-colônias — quando o módulo se quebra em partes especializadas.
  • Critério de saída — quando o módulo cumpre seu papel na fase.

Valem para todas as colônias sem exceção:

  1. Colônias não importam código umas das outras.
  2. Nenhuma colônia escreve no banco de dados de outra.
  3. Toda comunicação entre colônias ocorre exclusivamente via eventos no barramento. Exceção única: o BFF da D-1a pode chamar outras colônias via HTTP para operações síncronas iniciadas pelo front-end (login, atualização de perfil, cadastro e recusa de conselheiro). O BFF atua como proxy de entrada, nunca como lógica de negócio — ele não toma decisão sobre dados de outras colônias, apenas roteia comandos do usuário, publica eventos no barramento e mantém projeções locais de leitura.
  4. A única dependência permitida é com o núcleo: Event Bus e Registry.
  5. IA não decide nem prioriza. Publica com marcação de origem automática quando a confiança excede o limiar parametrizado, desde que o caminho para contestação humana seja imediato e visível. Abaixo do limiar, a publicação aguarda revisão. Acima, a revisão é reativa (contestar), não preventiva (aprovar). Atua como redução de atrito, não como autoridade.
  6. Todo output de processamento automático é marcado como automático, versionado e mantém acesso ao conteúdo bruto.
  7. Monolito modular no MVP. Cada colônia nasce sem dependência das demais, pronta para se tornar microsserviço quando o sistema escalar.

O MVP é um monolito modular. Cada colônia existe como módulo autônomo dentro de um único processo de servidor. A separação é lógica, não física: módulos não se importam entre si, não compartilham banco de escrita, comunicam-se exclusivamente via barramento interno.

A escolha é deliberada e tem dois objetivos simultâneos:

  1. Fechar o ciclo completo sem a complexidade operacional de microsserviços distribuídos — sem orquestração de containers por serviço, sem service discovery, sem latência de rede entre colônias.
  2. Servir como base de onboarding. O monolito modular é o mapa do sistema. Um dev novo lê um módulo e entende completamente o que aquela colônia faz, quais eventos consome, quais produz, onde termina sua responsabilidade. Quando o sistema evoluir para microsserviços, cada módulo já está pronto para ser extraído — sem refatoração de fronteira, apenas extração física.

A evolução natural é: módulo NestJS no monolito → microsserviço independente com o mesmo contrato de eventos. O dev que contribuiu no módulo é o candidato natural a manter o microsserviço.

  • Licença: AGPL-3.0. Copyleft forte para serviços de rede — se alguém modificar e operar publicamente, é obrigado a publicar o código fonte das alterações. Alinhado com o princípio de transparência radical do projeto.
  • Backend: TypeScript + NestJS. Cada colônia = um módulo NestJS com encapsulamento próprio (controllers, services, repositórios).
  • ORM: Prisma. Schema declarativo com tipos TypeScript auto-gerados. Migrations versionadas. O schema-first do Prisma casa com a arquitetura orientada a contratos de evento — o banco é a verdade, o código reflete o schema.
  • Banco de dados: PostgreSQL único. Cada colônia opera em schema ou conjunto de tabelas próprios — mesma instância, fronteiras claras no nível de schema. Nenhuma colônia faz query nas tabelas de outra.
  • Armazenamento de arquivos: MinIO (S3-compatible) via Docker. API idêntica ao AWS S3: presigned URLs, buckets, object keys baseadas em hash. Trocar de MinIO para AWS S3 (ou DigitalOcean Spaces, Cloudflare R2) é mudar 3 variáveis de ambiente (endpoint, access key, secret key). Zero acoplamento com provedor.
  • Event Bus MVP: barramento in-process. EventEmitter2 do NestJS como implementação de referência. Eventos persistidos em tabela append-only (core.event_log) no mesmo PostgreSQL. A troca para Kafka ou NATS JetStream, quando o sistema escalar, não altera as colônias — apenas a camada de publicação e consumo do barramento.
  • Cache e ranking: Redis para sorted sets do ranking (D-4) e cache geo (D-2 e L-2) na Fase 2. No MVP implementado, ranking e caches geo vivem em memória (Map), reconstruídos do estado próprio no boot. Redis não é usado no código atual.
  • IA local (MVP):
    • Transcrição de áudio: whisper-tiny via @huggingface/transformers (ONNX runtime) no mesmo processo Node.js do monolito. Pesos de ~75 MB. Timeout de 60s por transcrição.
    • Descrição de imagens: Florence-2-base (~1,06 GB fp32) via @huggingface/transformers no mesmo processo Node.js do monolito. Legenda por tarefa (<MORE_DETAILED_CAPTION> padrão). Timeout de 180s por descrição. Detecção de objetos e OCR disponíveis no modelo, como capacidade futura para a D-1c.
    • Ambos são abstraídos por serviços dedicados no módulo D-1b. Carregamento preguiçoso com cache em volume (modelos_d1b). Se indisponíveis, o pipeline segue com funcionalidade reduzida (sem transcrição/descrição, confiança reduzida).
  • Frontend: React + Vite + TypeScript. shadcn/ui + Tailwind CSS. Leaflet + react-leaflet para mapa (tiles vetoriais self-hosted e satélite Esri, trocáveis). Zustand para estado, React Hook Form para formulários. PWA com service worker para operação offline.
  • Autenticação MVP: Device ID anônimo (UUID v4 gerado pelo front-end). Login social Google como upgrade voluntário para perfil persistente. Gov.br e validação de vínculo entram na Fase 2.

Os termos se mapeiam diretamente para as fases do Formigueiro:

  • MVP (Produto Mínimo Viável) cobre a Fase 1 — ciclo completo local: demanda entra, é categorizada, entra no ranking, vira agenda, recebe conselheiro, tem andamento registrado publicamente. Inclui as colônias de empresa básicas para mapeamento e simulação salarial.
  • MLP (Produto Mínimo Adorável) cobre a Fase 2 — escala territorial, robustez institucional, validação de vínculo, votação, anti-fraude, análise de cobertura e incubação social.

A separação não é apenas técnica. O MVP precisa provar que um ciclo mínimo fecha. O MLP prova que o sistema escala.


O núcleo não processa demandas, não executa lógica de negócio e não toma decisões. Define as regras do jogo e garante que os eventos fluam de forma confiável entre todas as colônias.


Contexto e responsabilidade

O barramento de eventos é o canal único de comunicação do sistema. Toda colônia publica e consome exclusivamente por aqui. Nenhuma colônia se comunica diretamente com outra.

Não executa lógica de negócio. Não filtra conteúdo. Não decide quem pode publicar o quê — isso é responsabilidade do Registry.

Responsabilidades do barramento:

  • Receber eventos publicados por qualquer colônia.
  • Garantir a ordem de entrega dentro de cada tópico/stream.
  • Persistir o histórico de eventos de forma append-only. Nenhum evento é apagado.
  • Reexecutar eventos em caso de falha de consumidor: se uma colônia cair e voltar, processa o que ficou pendente.
  • Expor o histórico completo para auditoria e replay.

Estado próprio

O barramento possui o log de eventos. É a memória do sistema. Um evento publicado é permanente. Pode ser reprocessado, auditado e comparado com qualquer versão anterior.

Lógica central

evento chega ao barramento
→ valida tipo, versão e payload contra o Registry
→ atribui sequence number global
→ persiste no log (append-only)
→ entrega aos consumidores registrados para aquele tipo
→ falha de consumo registra na DLQ e o replay do boot reprocessa a partir do cursor da colônia

Técnicas e referências de implementação

  • MVP implementado: EventEmitter2 in-process com persistência append-only em core.event_log (PostgreSQL). Publicação valida tipo e payload contra o Registry, persiste e só então emite. Falhas de consumo vão para a DLQ (core.dead_letter_queue) e o replay do boot reprocessa a partir do cursor de cada colônia.
  • Escala: Apache Kafka, Redpanda ou NATS JetStream. Kafka é a referência mais documentada para event sourcing com retenção de log e consumer groups. Redpanda oferece API Kafka-compatível com menor overhead operacional.
  • Garantia de entrega: at-least-once como padrão. Consumidores devem ser idempotentes.
  • Ordenação: garantida por partição/tópico. Cada tipo de entidade (ex: demanda-id) mapeia para uma partição para manter sequência causal.

Critério de saída

Eventos publicados são persistidos, entregues a todos os consumidores registrados e reexecutados após falha sem perda. O log é auditável por ID de evento e por sequência temporal.

Documento detalhado: N-0a - Event Bus.md


Contexto e responsabilidade

O registry é o idioma oficial do sistema. Define os tipos de eventos que existem, os schemas de dados que cada tipo carrega e os contratos que cada colônia cumpre para publicar ou consumir.

Não executa lógica. Não valida conteúdo de negócio. Não decide quem pode usar o quê.

Responsabilidades:

  • Catalogar todos os tipos de evento com nome, versão e schema.
  • Expor contratos como pacotes ou documentação estruturada para cada colônia.
  • Versionar schemas: um schema alterado gera nova versão, nunca substitui a anterior.
  • Ser a fonte da verdade sobre o idioma do sistema.

Estado próprio

Catálogo de tipos de evento com versões e schemas. Histórico de todas as versões anteriores de cada schema.

Convenção de nomenclatura de eventos

{domínio}.{entidade}.{ação}
Exemplos:
demanda.recebida
demanda.categorizada
demanda.ranqueada
conselheiro.sorteado
agenda.gerada
vínculo.validado
voto.submetido
hash.checkpoint_publicado

Schema mínimo de um evento

{
"sequence_number": "bigint (BIGSERIAL, monotônico global)",
"event_id": "uuid-v4",
"tipo": "demanda.recebida",
"versao_schema": "1.0.0",
"timestamp": "ISO-8601",
"origem": "D-1a",
"correlacao_id": "uuid-da-sessão-ou-requisição",
"payload": { ... },
"criado_em": "ISO-8601"
}

Técnicas e referências de implementação

  • Schema definition: JSON Schema, Avro ou Protobuf. Avro é referência comum com Kafka (Schema Registry do Confluent ou Karapace como alternativa open source).
  • MVP implementado: JSON Schema com Ajv (draft 2020-12). O catálogo da Fase 1 vive em src/nucleo/n-0b-registry/schemas/ e é sincronizado no banco no boot de forma idempotente. O EventBusService.publicar() valida tipo, versão e payload contra o catálogo (tipos deprecated são rejeitados).
  • Distribuição: o registry se materializa como pacote versionado (npm, pip, ou equivalente) que cada colônia importa do núcleo — única dependência permitida.
  • Alterar o registry sem versionar é a única operação proibida no núcleo.

Critério de saída

Toda colônia consegue publicar e consumir eventos usando apenas os contratos do registry. Qualquer mudança de schema gera nova versão rastreável. Nenhuma colônia precisa conhecer a estrutura interna de outra.

Documento detalhado: N-0b - Registry.md


Contexto e responsabilidade

Camada transversal. Não é uma colônia de negócio, mas um requisito do núcleo desde o primeiro módulo. Sem observabilidade, erros em eventos são invisíveis e rastreamento de falhas é impossível.

Responsabilidades:

  • Logs estruturados em JSON: toda entrada e saída do barramento gera log com mensagem, correlacao_id, event_id, colonia e os dados do registro.
  • Trace distribuído por correlacao_id: uma demanda que passou por 6 colônias tem um trace único que mostra cada passo.
  • Métricas básicas: volume de eventos por tipo, latência de processamento por colônia, taxa de falha e tamanho da DLQ.
  • Alertas de anomalia: fila crescendo sem consumo, timeout de processamento, schema inválido.

Técnicas e referências de implementação

  • Logs: formato JSON estruturado, emitido pelo Logger do NestJS com serialização manual.
  • Trace: OpenTelemetry como padrão de instrumentação na Fase 2. Backend: Jaeger ou Zipkin.
  • Métricas: Prometheus + Grafana como referência de visualização.
  • No MVP implementado: spans de processamento com contexto de trace via AsyncLocalStorage (o TraceInterceptor propaga o correlacao_id nas requisições HTTP), métricas em memória expostas em formato Prometheus (GET /api/observability/metrics), gauge da DLQ vivo e health/readiness com checagem real de banco e MinIO. O trace por correlacao_id é materializado pelo N-0a (GET /api/events/trace/:correlacaoId), que consulta o core.event_log. OpenTelemetry/Jaeger e Prometheus/Grafana são referência para a Fase 2.

Critério de saída

É possível, dado um demanda_id, recuperar o histórico completo de eventos que ela gerou e em qual colônia cada um foi processado.

Documento detalhado: N-0c - Observabilidade.md


Objetivo: fechar um ciclo mínimo completo. Demanda entra, é categorizada, entra no ranking, vira agenda, recebe conselheiro e tem andamento registrado publicamente. Nenhum passo pode ser pulado.


Esta colônia é o ponto de entrada do sistema. O que chega aqui é dado bruto: texto livre, foto, áudio, localização aproximada, horário. O que sai daqui é uma demanda normalizada, com campos padronizados, pronta para o restante do pipeline.

A complexidade interna justifica a quebra em três sub-colônias especializadas.


Contexto e responsabilidade

Recebe o input do cidadão por qualquer canal disponível: app móvel, formulário web, SMS, ou integração com plataformas existentes (ex: 156). Valida os campos mínimos obrigatórios e publica o evento de entrada.

Não normaliza. Não categoriza. Não valida conteúdo.

Eventos produzidos

  • demanda.recebida (versão publicada 1.2.0) — payload: texto_bruto (vazio na captura só com mídia), tipo_midia (texto/foto/áudio), localizacao_bruta (lat/lng), midia_urls como campo adicional, assistencia como campo adicional de auditoria do revisor de texto, cidadao_id, canal, timestamp_criacao, termos_versao, consentimentos e os campos categoria_id e subcategoria_id da versão 1.1.0.
  • lugar.recebido — fluxo de lugares do mesmo BFF, com o dado bruto do lugar.
  • Eventos de identidade e do fluxo do conselheiro publicados pelo BFF: cidadão.cadastrado, cidadão.perfil_atualizado, cidadão.vinculado, conselheiro.cadastrado, conselheiro.atribuicao_recusada e conselheiro.atualização_registrada.

Estado próprio

Registro de demandas recebidas com status pendente_normalizacao. Apenas o dado bruto, sem processamento.

Lógica

cidadão submete input
→ valida campos mínimos (localização + descrição mínima)
→ gera demanda_id único
→ persiste dado bruto no estado próprio
→ publica demanda.recebida no barramento
→ retorna confirmação ao cidadão com demanda_id para acompanhamento

Técnicas e referências de implementação

  • Rate limiting por IP, com 5 demandas por minuto no POST de captura. Referência: token bucket ou sliding window counter.
  • Validação de localização: bounding box do território coberto. Rejeita coordenadas fora da área de operação.
  • Raio de captura: a posição final da demanda/lugar precisa estar a no máximo 10 km do fix do GPS do dispositivo (fix_lat/fix_lng opcionais no DTO; sem fix, a validação não roda). Impede relato de ponto distante do local de captura.
  • Canal de entrada como metadado persistido: útil para análise de padrões de acesso e cobertura territorial.
  • Idempotência: hash do conteúdo + cidadao_id, sem componente temporal. Submissão duplicada retorna HTTP 409 com o demanda_id existente (idempotência permanente, não janela curta). A identidade do cidadão anônimo chega via header X-Cidadao-Id (UUID v4 obrigatório na captura).
  • Assistência de texto: POST /api/assistencia/texto stateless no BFF, público, com rate limit de 10/min por IP, timeout de 8 s e degradação graciosa (disponivel: false). O campo adicional assistencia nos eventos de demanda, lugar e empresa registra o uso do revisor sem guardar o texto anterior nem o sugerido.

Critério de saída

Qualquer input válido de cidadão gera um demanda_id rastreável e o evento demanda.recebida no barramento dentro de tempo aceitável.

Documento detalhado: D-1a - Captura.md, D-1a - Front-end.md, D-1a - BFF.md


Contexto e responsabilidade

Consome demanda.recebida. Transforma o input bruto em um registro estruturado com campos padronizados. É aqui que texto livre vira campos semânticos, áudio vira transcrição e imagem vira descrição textual em campo próprio.

A autoria do texto é separada. A descricao_limpa publicada carrega exclusivamente o texto do cidadão e aceita string vazia; a descrição automática das imagens fica em midias_descritas e alimenta o título quando não há texto do cidadão.

A IA entra aqui como redução de atrito, nunca como decisão. Todo output automático é marcado como origem: 'automatico' e mantém acesso ao conteúdo bruto original.

Não categoriza. Não prioriza. Não valida vínculo.

Eventos consumidos

  • demanda.recebida

Eventos produzidos

  • demanda.normalizada (1.4.0; as versões 1.0.0 a 1.3.0 permanecem no catálogo) — payload contém: campos estruturados (título do cidadão ou da primeira legenda, descrição exclusivamente do cidadão, tipo de mídia processada, coordenadas validadas), confianca_normalizacao (0-1), demanda_id, conteudo_suspeito e termos_suspeitos. A 1.4.0 separa a autoria e aceita descricao_limpa vazia; midias_descritas traz a descrição e a tradução de cada imagem da captura. O conteúdo suspeito alimenta a fila da D-1d e fica fora da vitrine até a decisão de moderação; as descrições alimentam o item de anexo da fila e a projeção pública por evidência.

Estado próprio

Versão normalizada de cada demanda, com referência ao dado bruto original. O bruto nunca é descartado.

Lógica

consome demanda.recebida
→ se texto: limpa ruído (encoding, caracteres especiais), extrai título (primeiros N tokens significativos)
→ se áudio: transcreve para texto (Whisper local); sem texto do cidadão, a transcrição preenche a descrição
→ se imagem/foto: descreve cada imagem em sequência pelo modelo de visão local (Florence-2) e traduz cada legenda do inglês para o português (OPUS-MT), acumulando midias_descritas sem compor a descricao_limpa
→ sem texto do cidadão, o título usa a primeira legenda exibível truncada
→ valida coordenadas contra a bounding box única do território (a resolução fina fica na D-2)
→ aplica a denylist de texto sobre o título e a descrição do cidadão e marca conteudo_suspeito quando há termo
→ extrai entidades e calcula o score de confiança sobre o texto de análise (texto do cidadão e legendas exibíveis)
→ persiste versão normalizada
→ publica demanda.normalizada 1.4.0

Detecção de idioma por biblioteca é Fase 2. A validação geo da D-1b rejeita apenas pontos fora da bounding box nacional; a resolução fina do território fica na D-2.

Técnicas e referências de implementação

  • Transcrição de áudio: whisper-tiny via @huggingface/transformers (ONNX runtime), no mesmo processo Node.js, com timeout de 60s. Referência: Whisper (OpenAI, open source).
  • Descrição de imagem: Florence-2-base via @huggingface/transformers, com timeout de 180s, uma descrição por imagem da demanda em fluxo sequencial com o modelo já carregado. Se indisponível, o pipeline segue com confiança reduzida. A falha de uma imagem não impede as demais.
  • Tradução da legenda: OPUS-MT/Marian en→pt (Helsinki-NLP) via @huggingface/transformers, depois do Florence, imagem por imagem; ela alimenta a legenda exibível de midias_descritas e não compõe a descricao_limpa. A legenda original fica em processamento_detalhes.descricao_original e em midias_descritas. Motor desligado, modelo ausente ou texto já em português mantém o original, grava descricao_idioma: 'en' e penaliza a confiança da descrição. Cache no mesmo volume modelos_d1b.
  • Limpeza de texto: remoção de HTML, normalização Unicode, detecção de idioma com langdetect ou fastText.
  • Extração de entidades básicas: endereço, CEP, nome de rua. spaCy com modelo pt-BR ou regex estruturado para MVP.
  • Score de confiança: função heurística simples no MVP (campos presentes / campos esperados). Modelos de confiança mais sofisticados entram na Fase 2.
  • Denylist de texto: MODERACAO_DENYLIST_TEXTO com termos separados por vírgula, normalizados sem acento e em minúscula. Lista vazia ou ausente mantém o comportamento sem filtro. O resultado vira conteudo_suspeito e termos_suspeitos e alimenta a fila da D-1d.
  • Dado bruto preservado: armazenado com referência ao demanda_id. Sempre acessível. Normalização é uma visão derivada, não substituta.

Critério de saída

Toda demanda recebida tem uma versão normalizada com campos estruturados, score de confiança calculado e referência ao dado bruto preservado.

Documento detalhado: D-1b - Normalização.md


Contexto e responsabilidade

Gerencia o ciclo de vida dos arquivos anexados a demandas: fotos, documentos, áudio. Recebe o binário, valida, armazena com segurança e disponibiliza URL de acesso.

Não processa conteúdo semântico dos arquivos — isso é da sub-colônia de normalização. Apenas garante que o arquivo existe, é válido e está acessível.

Eventos consumidos

  • demanda.recebida (quando contém mídia)
  • demanda.evidencia_adicionada (evidências de confirmação e conclusão coletiva)
  • moderacao.decidida (decisão de anexo; a D-1c aplica no registro e publica anexo.moderado ou, na decisão removido, apaga o objeto e publica anexo.removido)

Eventos produzidos

  • anexo.processado (1.4.0; as versões 1.0.0 a 1.3.0 permanecem no catálogo) — payload: demanda_id, anexo_id, URL de acesso, tipo MIME, tamanho, hash SHA-256, object_key_temp da captura, via (captura|confirmacao|conclusao), possui_dado_sensivel, categoria_sensivel, motivo_sensivel e moderacao_status. O object_key_temp permite à D-1d casar a descrição automática da imagem com o item da fila e à D-7 casá-la com a evidência no resumo e no relatório.
  • anexo.moderado (1.0.0) — payload: anexo_id, status (ativo|bloqueado) e moderacao_status (aprovado|bloqueado).
  • anexo.removido (1.0.0) — payload: anexo_id, demanda_id e hash_sha256. Um evento por anexo afetado.

Estado próprio

Registro de anexos com metadados, URL de armazenamento e hash de integridade.

Lógica

recebe arquivo binário
→ valida tipo MIME contra lista permitida
→ valida tamanho máximo
→ calcula hash SHA-256
→ verifica duplicidade por hash (evita armazenamento redundante)
→ armazena com nome derivado do hash
→ classifica imagem (NSFW, pessoa e heurística de documento/PII)
→ bloqueia NSFW e enfileira a moderação quando sinalizado
→ registra metadados no estado próprio
→ publica anexo.processado
consome moderacao.decidida (tipo anexo)
→ aprovado ou bloqueado: aplica no registro e publica anexo.moderado
→ removido: apaga o objeto permanente e os temporários de todas as cópias do hash, publica anexo.removido por cópia e marca as linhas com status removido

Técnicas e referências de implementação

  • Armazenamento: S3-compatible (MinIO para self-hosted, S3 em cloud). URLs pré-assinadas com expiração para acesso controlado.
  • Validação de tipo: por magic bytes, não apenas extensão.
  • Limite de tamanho por tipo: imagem até 10MB, áudio até 5MB, application (documentos) até 20MB, texto até 5MB. Máximo de 10 mídias por demanda. Contrato de mídia da Fase 1: imagem e audio (vídeo não é aceito).
  • Privacidade de imagens: remoção de metadados EXIF, heurística de documento/PII via sharp e classificação local com AdamCodd/vit-base-nsfw-detector (NSFW, limiares 0,85 e 0,50) e Xenova/detr-resnet-50 (pessoa, sinalização a 0,50). Pessoa sinaliza sem bloquear; NSFW bloqueia e enfileira a moderação.
  • Deduplicação por hash: mesmo arquivo enviado por duas demandas diferentes ocupa espaço uma única vez.
  • Remoção física: a decisão removido da moderação apaga o objeto e todas as cópias do hash. O par hash_sha256 + demanda_id removido funciona como tombstone e bloqueia o re-upload do mesmo arquivo para a mesma demanda.
  • Disponibilidade pública: arquivos de demandas públicas têm URL acessível sem autenticação. Expiração configurável.

Critério de saída

Todo arquivo anexado tem URL de acesso estável, hash de integridade registrado e é acessível publicamente quando a demanda for pública. Arquivo removido pela moderação não existe mais no bucket e não pode ser reenviado para a mesma demanda.

Documento detalhado: D-1c - Gestão de Anexos.md


Contexto e responsabilidade

Organiza a revisão humana do conteúdo sinalizado pela classificação automática. Consome anexo.processado (imagem com NSFW, pessoa ou documento sinalizado), demanda.normalizada (texto com termo da denylist) e conselheiro.atualização_publicada (relato com termo da denylist), enfileira os itens e expõe os endpoints de decisão e de histórico para o moderador. Aprovar libera o conteúdo na vitrine. Bloquear mantém o conteúdo oculto e pode ser revertido. Remover apaga a mídia do armazenamento e limpa o texto dos schemas, sem reversão; o tipo relato recusa remoção.

Não classifica conteúdo e não executa a remoção física. A classificação fica na D-1c e na D-1b, e a denylist do relato roda na D-6b pelo helper compartilhado. A aplicação da decisão fica nas colônias donas do estado: a D-1c aplica no anexo e publica anexo.moderado ou anexo.removido; a D-7 libera, oculta ou limpa o texto, o relato e a trilha pública; a N-0d limpa o texto nos schemas de origem.

Eventos consumidos

  • anexo.processado (1.4.0), quando moderacao_status='pendente' ou possui_dado_sensivel=true
  • demanda.normalizada (1.4.0), quando conteudo_suspeito=true
  • conselheiro.atualização_publicada (1.2.0), quando conteudo_suspeito=true, como item relato

Eventos produzidos

  • moderacao.decidida (1.1.0) — payload: item_id, tipo (anexo|texto|relato), referencia_id, decisao (aprovado|bloqueado|removido), moderador_id, demanda_id?, motivo?, remover_texto_demanda? (só anexo) e reaberto? (reversão). A versão 1.0.0 permanece no catálogo para replay.

Estado próprio

Fila d1d.fila_moderacao com itens pendentes e decididos, histórico append-only d1d.decisoes_moderacao com uma linha por decisão ou reversão, descrições automáticas em d1d.descricoes_midia com upsert idempotente por demanda e chave da captura, mais d1d.eventos_processados (idempotência) e d1d.consumer_offset (cursor). A fila e o histórico mudam na mesma transação, com o evento_id reusado na publicação. O item de anexo guarda midia_object_key, a chave temporária que casa a descrição na listagem.

Lógica

consome anexo.processado / demanda.normalizada / conselheiro.atualização_publicada
→ persiste as midias_descritas em d1d.descricoes_midia, inclusive no descarte
→ sinalizado: enfileira com motivo, trecho e midia_object_key no anexo
→ não sinalizado: descarta e avança o cursor
→ a listagem casa a descrição por (demanda_id, midia_object_key) e monta descricao_imagem
→ moderador decide ou reverte em /admin/moderacao
→ grava a fila e o histórico na mesma transação
→ publica moderacao.decidida
→ D-1c aplica no anexo e publica anexo.moderado (ou anexo.removido, na remoção)
→ D-7 libera, oculta ou limpa o conteúdo, incluindo o relato
→ N-0d limpa o texto nos schemas de origem, na remoção

Endpoints

  • GET /admin/moderacao/fila — listagem paginada com filtros de tipo e status, incluindo removido (60/min, papel de moderação)
  • GET /admin/moderacao/fila/:item_id/historico — decisões e reversões do item em ordem cronológica, com decisão, motivo, moderador e data; 404 para item inexistente (60/min, papel de moderação)
  • POST /admin/moderacao/fila/:item_id/decidir — decisão com motivo opcional; na remoção o motivo é obrigatório e a flag remover_texto_demanda só vale para anexo; removido em relato responde 400 (60/min, papel de moderação)
  • POST /admin/moderacao/fila/:item_id/reverter — reverte aprovado ou bloqueado para a decisão oposta; item pendente ou removido responde 409 (60/min, papel de moderação)

Técnicas e referências de implementação

  • Fila com idempotência por evento_id e cursor de replay próprio (N-0a).
  • Decisão humana e auditável: a IA bloqueia por limiar e a revisão é reativa, no padrão da D-3.
  • Bloquear mantém a mídia no bucket e o conteúdo oculto. Remover é a exceção ao append-only, com motivo obrigatório, confirmação de irreversibilidade e hash preservado como impressão digital.
  • Histórico append-only com id abreviado do moderador na interface e a trava de concorrência no updateMany condicional com 409; a fila do web se atualiza em intervalo de 30 segundos com a aba visível.
  • Descrição da imagem: o card do anexo mostra a tradução em destaque e o original do Florence abaixo, rotulado com o idioma, sob o selo de descrição automática. Sem descrição, o bloco não renderiza, e ele não aparece em item de texto nem de relato. O selo não expõe confiança, porque o valor do Florence é constante.
  • Atalho para a demanda: o id no card abre /acompanhamento/:demanda_id em nova guia, com target="_blank" e rel="noreferrer", sem tirar o moderador da fila.
  • LGPD: a eliminação do titular anonimiza o trecho e o moderador da fila e do histórico e apaga as descrições automáticas das demandas do titular em d1d.descricoes_midia; a remoção por moderação sobrescreve o trecho e os termos do motivo.

Critério de saída

Todo conteúdo sinalizado entra na fila e recebe uma decisão registrada com moderador, data e trilha legível. O conteúdo aprovado volta à vitrine, o bloqueado permanece oculto e o removido sai do armazenamento e dos schemas sem possibilidade de reversão. O relato sinalizado fica oculto até a decisão. O item de anexo mostra a descrição automática da imagem quando ela existe, com a tradução em destaque e o original abaixo.

Documento detalhado: D-1d - Moderação.md


Contexto e responsabilidade

Consome demandas normalizadas e resolve a questão territorial: a qual unidade cívica essa demanda pertence? E a qual nível?

Não categoriza o conteúdo da demanda. Não prioriza. Apenas resolve a geometria territorial.

Eventos consumidos

  • demanda.normalizada

Eventos produzidos

  • demanda.georreferenciada — payload: demanda_id, unidade_civica_id de menor nível resolvida, nivel_minimo_resolvido (1-7), cadeia_ucs (lista de unidades cívicas pai, do menor ao maior nível), municipio_id opcional (UUID do nível 4 da cadeia, resolvido pela D-2 pelo nível dos polígonos), metodo_resolucao (gps, endereco ou inferencia; no MVP sempre gps), confianca_geo (alta, media ou baixa) e coordenadas_lat/coordenadas_lng opcionais.

Estado próprio

Cache de resoluções geo por coordenada. Base de polígonos das unidades cívicas (GeoJSON por nível).

Lógica

consome demanda.normalizada
→ extrai coordenadas (se disponíveis) ou endereço textual
→ se coordenadas: point-in-polygon contra base de polígonos de unidades cívicas
→ se endereço textual: geocodificação para coordenadas, depois point-in-polygon
→ resolve nível mínimo (ex: micro-unidade cívica / quadra)
→ resolve cadeia completa de unidades pai até nível nacional
→ calcula confiança_geo: sempre `alta` no MVP, porque toda coordenada chega por GPS. Os valores `media` (endereço textual) e `baixa` (inferência) variam na Fase 2, junto com o geocoding
→ ponto fora de todos os polígonos: persiste revisão pendente (`d2.revisoes_geo_pendentes`), não publica o evento de saída e não descarta em silêncio
→ registra resolução no cache
→ publica demanda.georreferenciada

Sub-colônias potenciais (se o volume territorial escalar)

A Fase 1 trata como colônia única. Com escala, pode se quebrar em:

  • Resolvedor de coordenadas: point-in-polygon puro.
  • Geocodificador: texto → coordenadas, com cache agressivo.
  • Validador de cobertura: verifica se a unidade cívica tem cobertura suficiente de dados (índice de subrepresentação, descrito na gestão).

Técnicas e referências de implementação

  • Base geográfica — seed em três camadas:
    1. IBGE (automático): malha municipal 2025 (níveis 4 a 7) e malha de bairros do Censo 2022 (nível 2) como carga única. 23.123 polígonos, 17.575 deles bairros, convertidos de shapefile para GeoJSON. A cobertura de bairros é parcial: 895 municípios e 25 UFs, sem Tocantins e sem o Distrito Federal.
    2. Mapeamento coletivo (contínuo): cidadãos desenham os polígonos dos níveis 1-2 (setores e bairros) no front-end via ferramenta de desenho (react-leaflet-draw). O polígono é submetido como lugar.recebido(tipo=poligono_uc), validado pela L-1 e inserido em core.uc_polygons. Este é o mecanismo de bootstrap territorial alinhado com a filosofia do projeto. O fluxo de desenho no front ficou fora do MVP e entra na Fase 2; a captura via API permanece disponível e inerte no MVP.
    3. OpenStreetMap (fallback, planejado): importação de relações admin_level=10 para bairros já mapeados. Cobertura parcial, complementar ao IBGE e ao mapeamento coletivo.
  • Validação implícita por uso: quanto mais demandas e lugares são georreferenciados dentro de um polígono desenhado pela comunidade, mais validação o polígono recebe. Polígonos inconsistentes geram evidência acumulada de erro (demandas caindo fora de todos os polígonos ou no polígono errado).
  • Point-in-polygon: Turf.js (JavaScript). Ray casting padrão, mesma biblioteca usada na validação de geometria do front-end, garantindo consistência.
  • Geocodificação: Nominatim (OpenStreetMap, self-hosted) ou Photon como alternativas open source. Fallback para API externa em casos de falha.
  • Cache: Map em memória no MVP para coordenadas resolvidas. Redis com TTL longo entra na Fase 2. A geometria dos polígonos não muda frequentemente.
  • Confiança geo: sempre alta no MVP (GPS do dispositivo). Endereço digitado (media) e inferência por texto (baixa) entram na Fase 2 com o geocoding. A confiança vira metadado da demanda para uso na priorização.

Critério de saída

Toda demanda normalizada tem unidade_civica_id resolvida e cadeia de unidades pai completa. Demandas com confiança geo baixa são marcadas e ficam visíveis no dashboard como “localização incerta”.

Documento detalhado: D-2 - Georreferenciamento.md


Contexto e responsabilidade

Classifica a demanda dentro da taxonomia de categorias do sistema. A taxonomia é estática na Fase 1 — definida nos parâmetros públicos, não gerada automaticamente. A colônia aplica a taxonomia, não a cria.

O resultado da categorização é o que permite ao ranking aplicar os pesos da tabela de precedência material. Sem categoria, não há ranqueamento.

Não define prioridade. Não altera o conteúdo da demanda. Não valida vínculo.

Eventos consumidos

  • demanda.normalizada — gatilho de processamento. O título, a descrição do cidadão e as legendas automáticas das imagens (midias_descritas) são a entrada do classificador.
  • demanda.georreferenciada — alimenta projeção local (d3.projecao_geo) que mapeia demanda_id ao unidade_civica_id.

Eventos produzidos

  • demanda.categorizada — payload: demanda_id, categoria_id, nivel_precedencia (1-5), score_horizontal, confianca_categorizacao, metodo (automatico/manual/revisao_pendente), sugestoes_alternativas (lista de {categoria_id, score_confianca} com até 5 candidatas), unidade_civica_id, nivel_minimo_resolvido.

Estado próprio

Categorização de cada demanda com histórico de versões. Se a categoria for corrigida manualmente, a versão anterior é preservada.

Taxonomia estática (Fase 1) — subconjunto MVP

O MVP implementa as seguintes categorias, extraídas da taxonomia completa (ver D-3 - Taxonomia.md). IDs e nomes são os mesmos do documento de referência.

O critério de inclusão é duplo: demanda universalmente reconhecível em qualquer bairro urbano do mundo e ciclo de resolução conduzível por um conselheiro de bairro sem depender de estrutura institucional específica por país.

Nível 1 — Sobrevivência básica
1.1 Abastecimento de água
1.2 Esgotamento sanitário
1.3 Energia elétrica
Nível 3 — Infraestrutura e serviços
3.1 Vias e pavimentação
3.2 Calçadas e acessibilidade física
3.3 Coleta de resíduos e limpeza urbana
3.4 Iluminação pública
3.9 Praças, parques e espaços públicos

Categorias como saúde, educação e segurança pública são universais como demanda, mas seus ciclos de resolução variam por estrutura nacional — ficam na taxonomia completa para o classificador sugerir, mas saem da tela principal do MVP. O classificador pode atribuir qualquer categoria da taxonomia completa a partir do texto livre do cidadão, mesmo que o front-end do MVP não a exiba como opção direta.

Scores horizontais, pesos e categorias são definidos e revisados pelos comitês técnicos de parametrização, nunca pela colônia de categorização.

Lógica

consome demanda.normalizada
→ aplica classificador sobre o texto de análise (título, descrição do cidadão e legendas automáticas das imagens)
→ gera lista de categorias candidatas com score de confiança para cada
→ seleciona categoria principal (maior score)
→ se confiança >= limiar_automático:
→ categoriza automaticamente com marcação de origem=automático
→ publica demanda.categorizada imediatamente
→ caminho de contestação fica visível na timeline: qualquer cidadão pode sinalizar "categoria errada?"
→ se confiança < limiar_automático e >= limiar_mínimo:
→ categoriza automaticamente com marcação de origem=automático
→ publica demanda.categorizada
→ mas também enfileira para revisão humana por amostragem (não bloqueia o pipeline)
→ se confiança < limiar_mínimo:
→ enfileira para revisão manual obrigatória
→ não publica demanda.categorizada até que um moderador confirme ou corrija
→ registra categorização com versão e método

Lógica de revisão manual

demanda.categorizada com metodo = 'revisao_pendente'
→ exibe para moderador com sugestões do classificador
→ moderador seleciona categoria ou cria nova (se justificada)
→ decisão do moderador gera nova versão da categorização
→ o par (texto, categoria_correta) alimenta o pipeline de melhoria do classificador

Técnicas e referências de implementação

  • MVP: classificador baseado em palavras-chave e regras por nível + categoria. Dicionário de termos mapeados para categoria_id. Rápido, explicável, zero dependência de modelo externo.
  • Evolução: TF-IDF + cosine similarity contra exemplos rotulados de cada categoria. Treinável com os dados corrigidos manualmente ao longo do tempo.
  • Fase 2+: fine-tuning de modelo de linguagem leve (ex: BERT multilíngue ou BERTimbau para português) sobre o corpus de demandas rotuladas. Mantém explicabilidade via atenção.
  • Limiar de confiança automático: parâmetro público. Valor inicial de referência: 0.85. Acima disso, a categorização é publicada imediatamente sem revisão humana, mantendo o caminho de contestação aberto na timeline.
  • Limiar mínimo de confiança: parâmetro público. Valor inicial de referência: 0.7. Abaixo disso, a publicação é bloqueada até revisão manual. Entre 0.7 e 0.85, a categorização é publicada automaticamente mas também enfileirada para revisão por amostragem.
  • Active learning: demandas com baixa confiança são priorizadas na fila de revisão manual. Demandas contestadas por cidadãos também entram na fila. Cada correção melhora o classificador.
  • O modelo de classificação é open source, versionado e auditável. Qualquer pessoa pode replicar a classificação de uma demanda com os mesmos dados de entrada.

Critério de saída

Toda demanda categorizada tem categoria_id, nivel_precedencia e score_horizontal definidos. Demandas com confiança baixa estão sinalizadas e em fila de revisão. A taxa de revisão manual cai ao longo do tempo com o acúmulo de dados rotulados.

Documento detalhado: D-3 - Categorização.md


Contexto e responsabilidade

Aplica a fórmula de priorização sobre as demandas categorizadas e georreferenciadas. Produz o ranking ordenado por unidade cívica. É a colônia que transforma dados em fila de trabalho.

Não executa agenda. Não aloca conselheiros. Não define política — aplica parâmetros definidos externamente.

Eventos consumidos

  • demanda.categorizada — gatilho primário do cálculo.
  • duplicidade.agregada (membros do agregado marcados inativos; ranking recalculado e republicado)
  • parâmetros.atualizados — stub no MVP: loga e avança o cursor. O recálculo dinâmico com novos parâmetros é Fase 2.
  • demanda.removida_por_votação — Fase 2, sem consumidor no MVP.
  • peso_situacional.atualizado — Fase 2, sem consumidor no MVP; reajuste a cada 18 meses.

Eventos produzidos

  • demanda.ranqueada (1.1.0) — payload: demanda_id, unidade_civica_id, categoria_id, nivel_precedencia, score_final, breakdown (peso_nacional, peso_situacional, score_horizontal), posicao_no_ranking, total_demandas_na_uc, versao_parametros e timestamp_calculo.
  • ranking.atualizado — publicado quando o ranking de uma unidade cívica muda de forma significativa (novo item, re-ranqueamento por votação, atualização de parâmetros, desativação de membros de agregado).

Estado próprio

Ranking atual por unidade cívica (write model de posições). Histórico de scores por demanda_id com versão dos parâmetros vigentes no cálculo. A tabela é append-only: membros de agregado permanecem com linha inativa (ativo = false), nunca são apagados.

Lógica central — fórmula de priorização

Score_final = Peso_nacional(nivel_precedencia)
× Peso_situacional(unidade_cívica, nivel_precedencia)
× Score_horizontal(categoria_id)

Peso nacional (nível de precedência) Valor estrutural por nível. Define a ordem macro. Nível 1 sempre à frente de nível 2, etc. Exemplo de referência:

Nível 1: 100
Nível 2: 80
Nível 3: 60
Nível 4: 40
Nível 5: 20

Peso situacional (dinâmico por unidade cívica) Reflete onde a unidade cívica ainda está deficiente. Se saneamento (nível 1) já está resolvido naquela unidade, o peso situacional para nível 1 cai. Reajustado a cada 18 meses com dados reais.

Peso_situacional(UC, nível) = 1 - (cobertura_atingida(UC, nível) / cobertura_meta(UC, nível))

Cobertura atingida é calculada com base em agendas concluídas e avaliações de resultado. Cobertura meta é parametrizada pelos comitês técnicos.

Score horizontal (por categoria dentro do nível) Posição relativa de cada tipo de demanda dentro do mesmo nível. Desempate dentro do nível.

Exemplo dentro do nível 1:
Água potável: 95
Saneamento: 85
Energia básica: 70
Habitação risco: 60

Distribuição por decaimento geométrico

A capacidade de conselheiros é distribuída entre todos os níveis por decaimento geométrico. O nível corrente recebe a maior fatia; os demais recebem fatias progressivamente menores conforme a distância. O fator f (referência: 0,40) define a razão entre fatias adjacentes. A distribuição normaliza para somar 100%.

Peso_bruto(j) = f ^ |j - nível_corrente| (onde f = 0,40)
Depois normaliza: cada nível recebe (peso_bruto / soma_total) da capacidade.

Exemplo com f = 0,40 e nível corrente = 1:

Nível Distância Peso bruto Capacidade
1 (corrente) 0 1,0000 61%
2 1 0,4000 24%
3 2 0,1600 10%
4 3 0,0640 4%
5 4 0,0256 2%

Soma dos pesos brutos = 1,6496. A normalização é exata em runtime — os percentuais acima são arredondados para exibição.

Nenhum nível fica a zero. Quando um nível vence, o seguinte assume a posição de corrente e a distribuição desliza sem salto. O fator f é parametrizável pelos comitês técnicos.

“Nível vencido” = peso_situacional(UC, nível) abaixo de limiar parametrizado (referência: 0.1).

Lógica de cálculo

para cada demanda_categorizada nova ou atualização de parâmetros:
→ recupera nivel_precedencia e score_horizontal da demanda
→ recupera peso_situacional atual da unidade_cívica
→ calcula score_final
→ registra breakdown completo (todos os fatores usados)
→ atualiza posição no ranking da unidade_cívica
→ publica demanda.ranqueada
→ se ranking mudou significativamente: publica ranking.atualizado

Auditabilidade do ranking

O breakdown é o mecanismo de explicabilidade. Para qualquer demanda, qualquer pessoa pode ver:

  • Qual peso nacional foi aplicado e por quê (nível da demanda).
  • Qual peso situacional foi aplicado e por quê (gap da unidade cívica naquele nível).
  • Qual score horizontal foi aplicado e por quê (categoria).
  • Qual versão dos parâmetros estava vigente no momento do cálculo.

Reproduzir o score manualmente, dado o breakdown, deve ser possível.

Técnicas e referências de implementação

  • Estrutura de dados do ranking: no MVP implementado, ranking em memória com posições persistidas no banco (d4.rankings) e reconstruído no boot. Redis ZADD é referência para a Fase 2.
  • Recalculação em batch vs. incremental: incremental por evento no MVP (recalcula só a demanda afetada). Recalculação global quando parâmetros mudam.
  • Versionamento de parâmetros: toda mudança de parâmetro gera uma versão nova. O breakdown persiste a versão dos parâmetros usados no cálculo — nunca apenas o score final.
  • Frequência de re-ranqueamento: configurável por nível de demanda (semanal para nível 1, mensal para nível 2, etc.).

Critério de saída

O ranking de qualquer unidade cívica é público, ordenado, com breakdown completo por demanda. Qualquer alteração de parâmetro gera recalculação rastreável com nova versão.

Limites do modelo

O ranking é algoritmo de prioridade. Não é instrumento orçamentário. Não define de onde virá o recurso para executar as demandas ranqueadas. No estágio inicial, o financiamento é privado — doações, vaquinhas e contribuições de entusiastas — sem dependência de orçamento público. O sistema é agnóstico quanto a quem financia e executa: a exigência é execução rastreável e gastos públicos. Demandas impostas por decisão judicial entram como prioridade externa, com registro visível de que não passaram pelo algoritmo padrão. Conselhos setoriais já existentes (saúde, assistência social, educação) podem fornecer parâmetros técnicos para os scores horizontais.

Documento detalhado: D-4 - Priorização e Ranking.md


Contexto e responsabilidade

Transforma o ranking em backlog de trabalho concreto para as unidades cívicas. Agrupa demandas territorialmente, organiza o backlog e publica o estado atual da fila de trabalho.

Não executa as demandas. Não aloca conselheiros (isso é a D-6). Não re-rankeia: apenas consome o ranking produzido pela D-4.

Eventos consumidos

  • demanda.ranqueada — gatilho primário: insere ou atualiza demanda no backlog
  • ranking.atualizado — dispara rebuild completo do backlog
  • demanda.georreferenciada — projeção local de coordenadas para agrupamento geográfico (DBSCAN)
  • conselheiro.sorteado (para marcar a demanda como atribuido e evitar re-sinalização para a D-6a)
  • conselheiro.demanda_iniciada (para marcar a demanda como em_progresso no backlog)
  • demanda.concluída (para retirar do backlog ativo)
  • demanda.conclusao_confirmada (conclusão coletiva: item fora de em_progresso passa a concluido com data e dias até a conclusão; os demais casos são ignorados com log)
  • duplicidade.agregada (membros marcados agregado; backlog regenerado)
  • demanda.removida_por_votação — stub da Fase 2: registrado sem cursor e fora do replay; apenas loga.
  • parâmetros.atualizados — stub da Fase 2: loga e avança o cursor.

Eventos produzidos

  • agenda.gerada — publicado quando o backlog de uma unidade cívica é (re)construído.
  • agenda.item_disponível — publicado quando uma nova demanda entra no topo do backlog, sinalizando para a colônia de conselheiros que há trabalho disponível.

Estado próprio

Backlog ordenado por unidade cívica, com projeção local de coordenadas (d5.coordenadas_demanda) e registro dos membros de agregado (d5.demandas_agregadas). Status de cada item: disponivel, atribuido, em_progresso, concluido e agregado; o status removido fica reservado à Fase 2. Itens agregado saem da ordenação e nunca geram agenda.item_disponível. Itens concluídos por qualquer via carregam data_conclusao e dias_ate_conclusao. Os grupos territoriais são efêmeros: vivem no resultado em memória do clustering e apenas o grupo_id gerado é persistido no item.

Conclusão coletiva

Quando demanda.conclusao_confirmada chega com ratificacao_necessaria: false, o handler conclui o item por status:

item inexistente → log.warn, cursor avança
item em_progresso → ignorado com log (aguarda ratificação na D-6b)
item agregado → ignorado com log (terminal)
item concluido → idempotente, cursor avança
demais status → status concluido + data_conclusao + dias_ate_conclusao
+ anúncio do próximo disponível da UC

Evento com ratificacao_necessaria: true não mexe no backlog: a conclusão aguarda a ratificação na D-6b, que então publica demanda.concluída pelo fluxo normal. Itens agregado e concluido são preservados em qualquer ordem de entrega.

Lógica

consome ranking.atualizado
→ recupera lista ordenada de demandas da unidade_cívica
→ aplica distribuição por decaimento geométrico
→ agrupa demandas por proximidade geográfica (clustering leve para otimizar deslocamento)
→ gera backlog ordenado com posição, score e agrupamento
→ publica agenda.gerada
→ para cada demanda nova no topo: publica agenda.item_disponível

Agrupamento territorial

Demandas próximas geograficamente, do mesmo nível e categoria, podem ser agrupadas para otimizar a atuação do conselheiro. O agrupamento é sugestivo, não mandatório. O conselheiro decide se trata como demanda única ou separada.

critério de agrupamento no MVP:
mesma unidade_cívica + mesma categoria + distância ≤ 500 m (raio padrão)
com no mínimo 2 demandas

Técnicas e referências de implementação

  • Agrupamento geográfico: DBSCAN com Haversine como métrica de distância, no GeoClustering em memória. Raio padrão de 500 m e mínimo de 2 pontos.
  • Visualização do backlog: o backlog é público. A projeção de leitura é um endpoint paginado com filtros por nível, categoria, status e território.
  • Agendas em backlog: demandas validadas e planejadas pelo ministério/secretaria responsável ficam em status disponivel até um conselheiro iniciar.

Critério de saída

O backlog de qualquer unidade cívica é público, acessível por API e atualizado em tempo real. A posição de cada demanda no backlog é justificada pelo score do ranking.

Documento detalhado: D-5 - Agenda.md


Esta colônia é responsável por toda a gestão do ciclo de atuação dos conselheiros: sorteio, atribuição de demandas, relatoria e acompanhamento. A complexidade justifica a quebra em duas sub-colônias com estados próprios distintos.


Contexto e responsabilidade

Gerencia o cadastro de conselheiros elegíveis, executa o sorteio quando necessário e atribui demandas disponíveis a conselheiros disponíveis respeitando a regra central: 1 conselheiro = 1 demanda ativa.

Não acompanha a execução. Não registra atualizações de status. Não avalia o conselheiro.

Eventos consumidos

  • agenda.item_disponível — sinaliza que há demanda esperando conselheiro.
  • conselheiro.cadastrado — novo candidato no sistema (v1.1.0 inclui campo capacitacao_concluida).
  • conselheiro.ciclo_concluído — conselheiro ficou disponível para nova atribuição.
  • conselheiro.atribuicao_recusada — conselheiro recusou a atribuição. Dispara re-sorteio e contagem para anti-acumulação. O motivo risco_pessoal (1.1.0) encerra e re-sorteia sem contagem e sem suspensão; recusa_explicita conta e suspende na quarta recusa (limite 3, 90 dias).
  • duplicidade.agregada (D-12) — remove da fila de pendentes os membros de agregados.
  • conselheiro.elegibilidade_atualizada — stub da Fase 2: loga e ignora. A mudança nos critérios (histórico de atuação, progressão de nível) entra na Fase 2.

Eventos produzidos

  • conselheiro.sorteado — payload: conselheiro_id, demanda_id, unidade_civica_id, metodo_sorteio, timestamp, seed_publico (para auditoria do sorteio).
  • sorteio.sem_candidatos — publicado quando há demanda disponível mas nenhum conselheiro elegível na unidade cívica. Aciona notificação.

Estado próprio

Cadastro de conselheiros com status (disponível/ocupado/inativo), nível de atuação, unidade cívica vinculada e histórico de atribuições. Identidade unificada no MVP: o id do registro é o cidadao_id. A colônia mantém um único endpoint de leitura do próprio estado: GET /d6a/conselheiros/me/situacao (ConselheiroGuard, 60/min).

Critérios de elegibilidade (Fase 1)

Fase 1:
- manifestou interesse
- completou capacitação básica (certificação simples)
- sem suspensão vigente
- não tem demanda ativa no momento
- a UC de atuação cobre ela mesma e as unidades menores dentro dela

Lógica do sorteio

agenda.item_disponível recebido para unidade_cívica X
→ monta a cadeia ascendente de X: [X, pai, avô, ..., raiz]
→ filtra conselheiros elegíveis por degrau, do exato à raiz; o primeiro degrau com elegíveis vence
→ se nenhum degrau tem elegíveis: publica sorteio.sem_candidatos
→ se há elegíveis:
→ gera seed público (HMAC-SHA256 do último `event_id` do barramento e do timestamp do momento)
→ ordena lista por seed (Fisher-Yates com seed determinístico)
→ seleciona primeiro da lista ordenada
→ atribui demanda ao conselheiro selecionado
→ atualiza status do conselheiro para "ocupado"
→ publica conselheiro.sorteado com o total_elegiveis do degrau vencedor

Auditabilidade do sorteio

O seed_publico é derivado de dados imprevisíveis no momento da atribuição: HMAC-SHA256 sobre o último event_id do barramento combinado com o timestamp ISO-8601 do momento. Quando o barramento está indisponível, cai para um UUID aleatório com log. Publicado junto com o resultado. Qualquer pessoa pode reproduzir a ordenação da lista e verificar que o conselheiro selecionado era o correto. Nenhuma parte do sistema pode predeterminar quem será sorteado.

Técnicas e referências de implementação

  • Geração de seed verificável: HMAC-SHA256 sobre o último event_id do barramento e o timestamp do momento. Referência de padrão: commit-reveal scheme simplificado.
  • Algoritmo de ordenação: Fisher-Yates shuffle com seed determinístico. Implementação em qualquer linguagem produz o mesmo resultado dado o mesmo seed.
  • Anti-acumulação: conselheiro não pode recusar a atribuição indefinidamente. Recusa gera registro. Mais de 3 recusas consecutivas suspende o cadastro por período parametrizável; suspensos com prazo vencido são reativados no boot.
  • Publicação com retry inline (publicarComRetry) em conselheiro.sorteado e sorteio.sem_candidatos.
  • Re-sorteio após recusa usa o snapshot da demanda persistido em d6a.atribuicoes (score_final, categoria_id, nivel_precedencia e grupo_id), evitando placeholders na fila de pendentes.
  • Sorteio serializado por fila global em memória; os pools de UCs sobrepostas compartilham conselheiros e o MVP roda em instância única.

Critério de saída

Todo sorteio é reproduzível por terceiros. A atribuição de uma demanda a um conselheiro tem seed público, lista de elegíveis e método documentados.

Documento detalhado: D-6a - Sorteio e Atribuição.md


Contexto e responsabilidade

Registra cada atualização feita pelo conselheiro sobre a demanda que acompanha: status, prazos, evidências, contatos realizados, entraves encontrados. Transforma o trabalho do conselheiro em eventos estruturados e públicos.

Não avalia o conselheiro. Não decide sobre a demanda. Não gera a timeline pública — isso é responsabilidade da D-7 transparência.

Eventos consumidos

  • conselheiro.sorteado (inicia o acompanhamento; guarda de sorteio tardio: demanda já concluída socialmente não cria acompanhamento)
  • conselheiro.atualização_registrada (publicado pela interface do conselheiro)
  • demanda.conclusao_confirmada (com acompanhamento ativo, grava pendencia_conclusao e registra entrada na relatoria; sem acompanhamento, apenas avança o cursor)

Eventos produzidos

  • conselheiro.demanda_iniciada — conselheiro fez o primeiro contato formal (telefonou, protocolou, agendou). É este evento que muda a demanda de atribuido para em_progresso na D-5.
  • conselheiro.atualização_publicada — payload: atualizacao_id, demanda_id, conselheiro_id, tipo, texto estruturado, campos estruturados, origem da estruturação, descricao_sanitizada e numero_sequencial. A versão 1.2.0 acrescenta conteudo_suspeito e termos_suspeitos quando a denylist de texto encontra termo; a 1.1.0 marca a transcrição automática e a 1.0.0 cobre o relato digitado limpo.
  • conselheiro.prazo_próximo — alerta publicado N dias antes do prazo estimado. Disparado por CronJob a cada 6 horas.
  • demanda.concluída — publicado quando o conselheiro marca a demanda como resolvida. Consumido pela D-5 para transição do backlog para concluido.
  • conselheiro.ciclo_concluído — ciclo de atuação encerrado (demanda concluída, fim de mandato ou desistência). Publicado após demanda.concluída quando a demanda é resolvida; publicado isoladamente nos demais casos. Consumido pela D-6a para liberar o conselheiro. As duas saídas de conclusão são persistidas em saidas_conclusao antes da publicação e republicadas pela varredura do boot quando a publicação falha.
  • demanda.resumo_ciclo_atualizado (1.0.0) — resumo público do ciclo, com demanda_id, resumo, total_atualizacoes, data_inicio, data_fim, versao_metodo, gerado_em e fontes. Publicado a cada atualização publicada e no fecho do ciclo, com o texto sanitizado antes de persistir e publicar.

Estado próprio

Histórico de atualizações por demanda. Status atual de cada acompanhamento. Prazos registrados e alertas pendentes. pendencia_conclusao no acompanhamento (null sem pendência; { conclusao_id, total_conclusoes, data } com pendência). Snapshot saidas_conclusao com as duas saídas de conclusão (demanda.concluída e conselheiro.ciclo_concluído) e a flag saidas_conclusao_pendentes, que a varredura do boot republica quando a publicação falha. Tabela d6b.conclusoes_sociais com as conclusões confirmadas que não exigem ratificação, para a guarda de sorteio tardio. A coluna texto_resumo_ciclo guarda o resumo público do ciclo, atualizado a cada publicação e no fecho. O dossiê do caminho chega ao conselheiro pela projeção da D-7; a D-6b não consome caminho.atualizado no MVP. As atualizações do mesmo acompanhamento são serializadas por um lock em memória; o lock distribuído fica para a Fase 2.

Ratificação de conclusão coletiva

Rota POST /d6b/demandas/:demanda_id/ratificar-conclusao com ConselheiroGuard (JWT com sessão Google, 5/min, fora do prefixo api). Validações na ordem: sessão Google (401); acompanhamento ativo da demanda (404); pendência registrada (409); o cidadão é o conselheiro do acompanhamento (403). O sucesso reusa o fluxo de conclusão existente: publica demanda.concluída e conselheiro.ciclo_concluído com motivo_encerramento: 'demanda_concluida', limpa a pendência e registra a conclusão social. A conclusão por atualização normal do conselheiro também limpa a pendência. Sem motivo de encerramento novo.

Tipos de atualização (estruturados)

contato_realizado → com quem, canal, resultado
protocolo_aberto → número, órgão, data
documento_anexado → tipo, descrição, referência ao anexo
entrave_registrado → descrição do bloqueio, órgão envolvido
status_atualizado → pendente / em_andamento / aguardando / concluido / bloqueado
prazo_registrado → data estimada de resolução, fonte da estimativa

Lógica de normalização de atualização

conselheiro registra atualização via interface (front-end → BFF D-1a)
→ front-end chama endpoint REST da D-6b para sugestão de IA (POST /d6b/sugerir-estruturacao)
→ D-6b retorna sugestão com campos estruturados e score de confiança
→ conselheiro revisa, edita e confirma no front-end
→ BFF publica conselheiro.atualização_registrada no barramento
→ D-6b consome, valida, persiste e verifica o texto na denylist de texto
→ publica conselheiro.atualização_publicada (1.2.0 com suspeição, 1.1.0 na transcrição limpa, 1.0.0 no relato digitado limpo)
→ se primeira atualização do acompanhamento: publica também conselheiro.demanda_iniciada
→ se tipo for status_atualizado com conclusão: publica demanda.concluída + conselheiro.ciclo_concluído

IA na relatoria

A IA sugere como estruturar o texto da atualização (separar fato de interpretação, padronizar linguagem neutra) via endpoint REST dedicado (POST /d6b/sugerir-estruturacao, sob ConselheiroGuard, 10/min), chamado sincronamente pelo front-end do conselheiro. A sugestão é stateless: não persiste dados. O conselheiro sempre revisa e confirma antes da publicação. A sugestão original é preservada no payload do evento conselheiro.atualização_registrada para auditoria: o que a IA sugeriu vs. o que o conselheiro publicou. Se a IA não estiver disponível, opera em modo fallback com heurísticas de extração de campos. Em todos os casos, a sugestão é marcada como automática e o texto final publicado é do conselheiro. A IA nunca publica sem que o conselheiro tenha visto e confirmado a tela de revisão.

Critério de saída

Cada demanda ativa tem histórico de atualizações rastreável com timestamp, tipo estruturado e referência ao conselheiro responsável. O primeiro ato formal do conselheiro (iniciação) gera evento público imediato.

Documento detalhado: D-6b - Relatoria e Acompanhamento.md


Contexto e responsabilidade

Consolida o histórico de eventos de cada demanda em visões públicas de leitura. É a colônia de output principal para cidadãos, pesquisadores e auditores. Aplica o padrão CQRS: write model está nas colônias de origem, aqui ficam as projeções de leitura.

Não processa dados novos. Não toma decisões. Consome eventos de todas as demais colônias e projeta o estado legível.

Eventos consumidos

  • 30 tipos em produção: demanda.recebida, demanda.normalizada, demanda.categorizada, demanda.georreferenciada, demanda.ranqueada, ranking.atualizado, agenda.gerada, agenda.item_disponível, conselheiro.sorteado, conselheiro.demanda_iniciada, conselheiro.atualização_publicada, conselheiro.prazo_próximo, demanda.concluída, conselheiro.ciclo_concluído, lugar.cadastrado, lugar.georreferenciado, demanda.confirmada, demanda.evidencia_adicionada, duplicidade.candidata_detectada, duplicidade.agregada, anexo.processado, demanda.conclusao_confirmada, anexo.moderado, moderacao.decidida, anexo.removido, lugar.validado, lugar.desativado, demanda.resumo_agregado_atualizado, demanda.resumo_ciclo_atualizado e caminho.atualizado.
  • Três stubs da Fase 2, fora do replay: demanda.removida_por_votação, votação.resultado_publicado e hash.checkpoint_publicado.

Os eventos de demanda_id alimentam a timeline e o snapshot. lugar.cadastrado e lugar.georreferenciado alimentam d7.lugares_geo; residências e poligono_uc são descartados com cursor avançado. Os eventos do agregado projetam confirmações, evidências e representante. As decisões de moderação liberam, ocultam ou limpam o conteúdo, inclusive o relato do conselheiro suspenso pela denylist, que nasce interno e só entra na vitrine após a aprovação. Os dois resumos e o caminho atualizam o snapshot e a tabela de perfis, sem entrada de timeline, para não duplicar na vitrine o texto derivado que já aparece nos resumos.

Eventos produzidos

  • Nenhum. Colônia de leitura pura. Serve queries, não produz eventos de negócio.

Estado próprio

Projeções de leitura desnormalizadas (otimizadas para query, não para escrita):

  • Timeline por demanda_id: lista cronológica inversa de todos os eventos (mais recentes primeiro), com descrição legível.
  • Dashboard por unidade_civica_id: contagens, médias, distribuição por status e categoria.
  • Snapshot por demanda_id (d7.demanda_snapshots): título, coordenada_lat e coordenada_lng (de demanda.recebida.localizacao_bruta, sobrescritas por demanda.georreferenciada), status, categoria, UC, municipio_id (campo municipio_id de demanda.georreferenciada, com fallback pelo quarto elo da cadeia_ucs), total_confirmacoes, agregado_representante_id, agregado_membros_ids, resumo_agregado, resumo_ciclo, resumos_atualizado_em, caminho_dossie e descricoes_midia ([{ object_key, descricao }], com a descrição automática de cada imagem, sanitizada e truncada em 2.000 caracteres). A descricao pública do snapshot é exclusivamente o texto do cidadão; a descrição das imagens aparece por evidência e no bloco marcado como IA.
  • d7.caminhos: perfil por (município, subcategoria) alimentado por caminho.atualizado, com contagem de casos, prazo mediano, canais, órgãos, gargalos, documentos, dossiê e versao_metodo. O caminho_dossie do snapshot é a cópia denormalizada do perfil para a demanda, e os endpoints servem o caminho pela chave da demanda, com fallback para a categoria.
  • d7.demanda_evidencias: evidências da captura, das confirmações e da conclusão, com via, object_key e object_key_temp; a URL é assinada na leitura, com validade de 5 minutos. O object_key_temp casa a evidência com descricoes_midia e a leitura devolve a descricao da imagem; evidência sem par fica sem o campo.
  • d7.lugares_geo: lugares públicos (organização e equipamento público) com coordenadas, tipo, nome, subtipo declarado e confirmado, descricao, horario_funcionamento, UC resolvida, status, status_confianca (default provisorio), metodo_validacao, total_confirmacoes e confianca_tipificacao. O lugar provisório entra na projeção e aparece no mapa com marcação distinta; disputado ou desativado sai da listagem pública e o resumo devolve 404.
  • Visão pública de um conselheiro: demandas acompanhadas, status, tempo médio (Fase 2).
  • Polígonos de UC são lidos de core.uc_polygons no endpoint de listagem (exceção controlada, read-only).
  • A decisão removido limpa a projeção: o snapshot perde título, descrição, última atualização e descricoes_midia, as entradas internas recebem a marca de remoção, a timeline ganha a entrada pública e a evidência removida sai de d7.demanda_evidencias com a entrada correspondente de descricoes_midia. Como o log já foi tombstonado pela N-0d, o rebuild não ressuscita conteúdo nem evidência; as descrições de mídia, preservadas em claro pela redação, sobrevivem ao replay. A eliminação e a retenção do titular também zeram descricoes_midia.
  • O relato do conselheiro entra na timeline com dados_relevantes.atualizacao_id. Com conteudo_suspeito=true, a entrada nasce interna e o texto_ultima_atualizacao não é tocado; a aprovação publica as entradas do atualizacao_id e repõe o texto da última atualização truncado em 200 caracteres; a reversão para bloqueado oculta de novo e zera o texto.

Endpoints públicos

  • GET /api/d7/timeline/:demanda_id (60/min) — timeline completa da demanda, paginada, em ordem cronológica inversa.
  • GET /api/d7/dashboard/:uc_id (30/min) — dashboard de indicadores agregados da unidade cívica.
  • GET /api/d7/demandas (60/min) — demandas por bounding box; exclui removida, agregada e concluida no filtro padrão, aceita status explícito, inclusive concluida, e devolve total_confirmacoes e agregado_representante_id por linha. O mapa mostra só o que está em aberto.
  • GET /api/d7/lugares (60/min) — lugares por bounding box, com status_confianca por linha e filtro de disputado e desativado; residências nunca são retornadas (LGPD).
  • GET /api/d7/lugar/:lugar_id/resumo (60/min) — resumo público do lugar. 404 para lugar inexistente, disputado ou desativado.
  • GET /api/d7/uc/poligonos (30/min) — polígonos de UC por nível opcional, uc_id e nome normalizado (sem acento, caixa ou pontuação); cada linha devolve uf? e parent_nome?, e incluir_geometria (padrão true) permite respostas sem GeoJSON. O seletor do web mostra SP · São Paulo · Pinheiros.
  • GET /api/d7/demanda/:demanda_id/resumo (60/min) — título e descrição públicos quando projetados, subcategoria resolvida pela taxonomia, contador de confirmações, membros do agregado, evidências com URL de 5 minutos e descricao opcional por imagem, coordenadas opcionais, as marcas conteudo_removido e conteudo_removido_em e os campos resumo_agregado, resumo_ciclo, resumos_atualizado_em e caminho quando projetados.
  • GET /api/d7/demanda/:demanda_id/relatorio (30/min) — relatório da demanda: identificação, descrição pública sanitizada do cidadão (quando projetada), categoria com nome e área da taxonomia, subcategoria com a descrição formal, status com descrição, datas, coordenadas, score e posição corrente no ranking, contador de confirmações, agregado, evidências (com descricao opcional por imagem), timeline pública completa, dias em aberto, responsáveis nos três níveis federativos e os mesmos resumo_agregado, resumo_ciclo, resumos_atualizado_em e caminho do resumo. O acompanhamento e o relatório exportado mostram a mesma visão.
  • GET /api/d7/uc/:uc_id/relatorio (15/min) — demandas da UC agregadas por categoria, com filtros opcionais de categoria, status e janela temporal, demanda mais antiga por categoria e responsáveis. A matéria-prima vem das projeções existentes: snapshots, timeline, evidências e o mapa shared/taxonomia/responsaveis-categorias.ts.
  • GET /api/d7/uc/:uc_id/historico (60/min) — histórico público paginado das demandas concluídas da UC e das unidades menores dentro dela, com status fixo concluida, filtros de categoria, janela por data_conclusao e título, e ordenação por data de conclusão decrescente. É a consulta do que já foi resolvido; o mapa fica com o que está em aberto.
  • GET /api/d7/ranking/:uc_id (60/min) — ranking público da UC, ordenado por score final, com busca opcional por trecho do título. Agregadas, removidas e concluídas não aparecem.
  • POST /api/d7/rebuild (1/hora, PapelOperadorGuard) — rebuild síncrono das projeções via replay; responde 409 quando já há uma reconstrução em andamento.

Contratos completos no documento detalhado, seções 1.8 a 1.12.

Lógica de projeção

para cada evento relevante:
→ mapeia evento para descrição legível em português
→ adiciona ao log cronológico da demanda
→ atualiza agregados do dashboard da unidade_cívica
→ se evento é público: marca como visível sem autenticação
→ persiste projeção no banco de leitura

Exemplos de descrição legível na timeline

evento: demanda.recebida
→ "Demanda registrada via app em 14/03/2026 às 10:32"
evento: demanda.categorizada (método=automático)
→ "Categorizada automaticamente como: Saneamento básico (nível 1) — confiança 82%"
evento: conselheiro.sorteado
→ "Conselheiro designado via sorteio em 15/03/2026"
evento: conselheiro.demanda_iniciada
→ "Conselheiro fez o primeiro contato com a Prefeitura em 16/03/2026"
evento: conselheiro.atualização_publicada (tipo=entrave)
→ "Entrave registrado: aguardando autorização da SABESP — 20/03/2026"

Dashboard básico — indicadores da Fase 1

por unidade_cívica:
total de demandas ativas
distribuição por nível de precedência
distribuição por status
tempo médio de início (da recepção até a primeira ação do conselheiro)
tempo médio de conclusão (das demandas já encerradas)
número de conselheiros ativos
taxa de demandas com confiança geo baixa (sinaliza necessidade de revisão)

Técnicas e referências de implementação

  • Banco de leitura: PostgreSQL com índices otimizados para as queries do dashboard. Ou ElasticSearch para queries de texto e geoespaciais.
  • Padrão CQRS: os eventos são a fonte da verdade. O banco de leitura é descartável e reconstruível a qualquer momento replaying todos os eventos do barramento. Isso é o que garante que a projeção pública nunca diverge do estado real — pode ser reconstruída do zero se necessário.
  • API pública: endpoints REST com paginação. Nenhuma autenticação necessária para dados públicos. Rate limiting para proteção contra scraping massivo.
  • Internacionalização das descrições: os textos legíveis são templates parametrizados. Trocar o idioma é trocar os templates, sem alterar a lógica de projeção.

Critério de saída

Qualquer cidadão consegue acessar a timeline completa de qualquer demanda pública pelo demanda_id. O dashboard de qualquer unidade cívica está acessível sem autenticação e atualizado em tempo próximo ao real. O histórico das demandas concluídas de uma unidade cívica está acessível por UC, sem autenticação.

Documento detalhado: D-7 - Transparência.md


Fase 1 (embarcada no BFF) → Fase 2 (microsserviço independente)

Contexto e responsabilidade

A C-1 é a fonte da verdade da identidade do cidadão e de suas autodeclarações de perfil. Responde a duas perguntas: quem é o cidadao_id que está operando e o que esse cidadão declara sobre si? Gerencia os providers de autenticação (device anônimo, Google, gov.br na Fase 2) e mantém os metadados de perfil: nome, email, avatar, endereço autodeclarado e unidade cívica de residência autodeclarada.

A C-1 guarda a declaração. A verificação é outra camada. Quando o cidadão declara “moro na Rua X, UC Jardim das Flores”, a C-1 armazena. Na Fase 2, a D-9 (Validação de Vínculo) verifica independentemente essa declaração (documento, validação social ou presença) e publica o resultado. A C-1 e a D-9 são donas de entidades distintas no mesmo fluxo. A D-9 referencia o par cidadao_id + uc_id declarado na C-1 e emite vínculo.validado com status, método e hash da evidência.

Não valida vínculo, não gerencia elegibilidade de conselheiro, não toma decisão sobre quem pode fazer o quê. Apenas estabelece identidade e armazena autodeclarações.

No MVP (Fase 1), a C-1 não existe como módulo separado. Os dados de cidadão residem no estado próprio do BFF da D-1a. É uma tabela encapsulada dentro do módulo. O BFF é o dono natural porque é ele quem gera o cidadao_id e gerencia os providers de autenticação.

Na Fase 2, quando o monolito se parte em microsserviços, a tabela é extraída para a C-1 como serviço independente. O BFF passa a consumi-la por API síncrona (login, atualização de perfil) e por eventos (projeções locais de leitura). Nenhuma outra colônia acessa o banco da C-1.

Eventos produzidos

  • cidadão.cadastrado — novo registro criado com identidade básica.
  • cidadão.perfil_atualizado — nome, email, avatar, endereço ou UC de residência alterados.
  • cidadão.vinculado — Device ID anônimo vinculado a uma conta Google/gov.br.

Eventos consumidos

  • vínculo.validado (D-9) — a C mantém projeção local do status de vínculo para exibição no perfil.

Estado próprio

Registro de cidadãos com:

  • id, nome, email, avatar_url, auth_provider
  • Mapeamento de providers (google_sub, govbr_sub)
  • Endereço autodeclarado e unidade_civica_id de residência (opcionais, autodeclarados)
  • Projeção local: status de vínculo por UC (consumido da D-9)

Critério de saída

Toda colônia que precisa de metadados de cidadão ou de status de vínculo os obtém via cidadao_id. A C-1 não armazena dados pessoais além do mínimo necessário. Documentos e evidências de vínculo são domínio exclusivo da D-9.

Documento detalhado: C-1 - Cadastro de Cidadãos.md


Contexto e responsabilidade

Detecta demandas equivalentes e as agrega sob um representante, por confirmação coletiva. Reduz ruído no ranking e no mapa: a mesma necessidade ocupa uma posição, não várias. No MVP, a detecção é heurística determinística. A similaridade semântica por embeddings entra na Fase 2.

Não rejeita, não mescla e não decide sozinha. Sinaliza candidatas e executa a agregação que a confirmação coletiva autorizou.

Eventos consumidos

  • demanda.recebida (grava o relator privado)
  • demanda.normalizada (descrição do cidadão, legendas automáticas das imagens e coordenadas)
  • demanda.categorizada (categoria; UC da versão 1.1.0)
  • demanda.georreferenciada (UC e coordenadas)
  • demanda.concluída (status terminal)
  • conselheiro.sorteado (status atribuida)
  • duplicidade.agregada (status agregada)
  • demanda.conclusao_confirmada (própria saída, no replay e no rebuild: conclui o índice quando a ratificação é desnecessária)
  • cidadão.vinculado (funde relator, confirmações e conclusões do device anônimo no cidadao_id da conta Google)

Eventos produzidos

  • duplicidade.candidata_detectada — candidatas com similaridade, distância, categoria e UC; origem: 'automatico', versao_modelo e critérios no payload.
  • demanda.confirmadatotal_confirmacoes novo e mecanismo (mapa | captura). Sem cidadao_id.
  • demanda.evidencia_adicionada — evidência anexada (imagem | audio, URL pública, via: 'confirmacao' | 'conclusao').
  • duplicidade.agregada — representante e membros, mecanismo: 'confirmacao_coletiva'. Sem cidadao_id.
  • demanda.conclusao_confirmada — conclusão coletiva no limiar: demanda_id, unidade_civica_id, conclusao_id, total_conclusoes, data_conclusao, mecanismo: 'confirmacao_coletiva', ratificacao_necessaria. Sem cidadao_id, sem evidência, sem coordenada.
  • demanda.resumo_agregado_atualizado (1.0.0) — resumo público do agregado, com demanda_id do representante, membros, resumo, total_relatos, periodo, versao_metodo, gerado_em e fontes. Publicado no fecho do agregado e a cada membro novo, com o texto sanitizado antes de persistir e publicar.

Estado próprio (schema d12)

  • d12.demanda_indice: texto normalizado e tokens, categoria, UC, coordenadas, status, relator_cidadao_id (privado), conclusao_confirmada_publicada, data_recebida, ultimo_event_id.
  • d12.candidatas: pares com similaridade, distância, categoria e UC.
  • d12.confirmacoes: cidadao_id e localização de presença (privados), evidência, mecanismo, verificada.
  • d12.conclusoes: cidadao_id e localização de presença (privados), evidência, verificada, evento_id; unique [demanda_id, cidadao_id].
  • d12.agregados: representante, membros, critérios e resumo_agregado (texto público do agregado).
  • d12.consumer_offset.

O índice guarda descricao_limpa apenas com o texto do cidadão; a comparação usa a descrição e as legendas automáticas das imagens (midias_descritas, tradução quando aplicada), com fallback para o título. O resumo agregado segue lendo a descricao_limpa do cidadão.

Lógica de detecção (heurística MVP)

consome demanda.categorizada ou demanda.georreferenciada
→ índice completo (UC + categoria + coordenadas + texto)
→ compara com demandas da mesma UC e categoria,
excluindo status atribuida, concluida e agregada
→ Haversine própria ≤ 500 m + Jaccard ≥ 0,6 (unigrams + bigrams, stopwords PT)
→ par novo vira linha em d12.candidatas
→ publica duplicidade.candidata_detectada

Lógica de confirmação e agregação

confirmação via POST /api/d12/demandas/:id/confirmacoes
→ identidade: JWT com auth_provider='google' tem precedência; sem JWT, X-Cidadao-Id anônimo
(400 sem header, 401 com Bearer inválido)
→ demanda existe no índice (404) e tem status não terminal (409)
→ não é o relator (400); cidadão ainda não confirmou (409)
→ presença: Haversine entre cidadão e demanda ≤ 700 m (400)
→ anônimo com total atual ≥ 2 recebe 401 login_necessario; a terceira exige conta Google verificada
→ persiste confirmação; publica demanda.confirmada (+ evidencia_adicionada por item)
→ com candidatas registradas, total ≥ 3 e ao menos uma confirmação verificada:
→ representante = mais confirmações (desempate: data_recebida mais antiga)
→ membros passam a status agregada
→ publica duplicidade.agregada

Lógica de conclusão coletiva

conclusão via POST /api/d12/demandas/:id/conclusoes
→ identidade: JWT com auth_provider='google' tem precedência; sem JWT, X-Cidadao-Id anônimo
(400 sem header, 401 com Bearer inválido)
→ demanda existe no índice (404); status terminal ou agregada rejeita (409)
→ não é o relator (400); cidadão ainda não concluiu (409, unique no banco)
→ presença: Haversine entre cidadão e demanda ≤ 700 m (400)
→ evidência obrigatória de 1 a 3 mídias (400 sem evidência)
→ anônimo com total atual ≥ 2 recebe 401 login_necessario; a terceira exige conta Google verificada
→ persiste conclusão; publica demanda.evidencia_adicionada (1.3.0, via: 'conclusao') por item
→ quando o total cruza 3 (CONCLUSAO_CONFIRMACOES_MINIMAS):
→ ratificacao_necessaria = (status do índice === atribuida)
→ publica demanda.conclusao_confirmada se ainda não publicada e houver ao menos uma conclusão verificada
→ sem ratificação: índice passa a concluida (status monotônico, não regride atribuida)
→ índice grava conclusao_confirmada_publicada = true (restaurado no rebuild pelo próprio evento)

Endpoints REST

  • POST /api/d12/demandas/:demanda_id/confirmacoes (5/min) — identidade por JWT (precedência) ou header X-Cidadao-Id (UUID v4; 400 sem header, 401 com Bearer inválido). Anônimo com total atual ≥ 2 recebe 401 login_necessario. DTO com localização e evidência opcional (máx. 3).
  • POST /api/d12/demandas/:demanda_id/conclusoes (5/min) — identidade igual à confirmação; anônimo com total atual ≥ 2 recebe 401 login_necessario. DTO com localização e evidência obrigatória (1 a 3 mídias com URL validada). 201 devolve { conclusao_id, total_conclusoes, ratificacao_necessaria }.
  • POST /api/d12/candidatas (30/min) — busca prévia de candidatas por texto, categoria e coordenada; devolve top 5 por similaridade, depois distância.
  • POST /api/d12/rebuild (1/hora, operador) — trunca demanda_indice, candidatas e agregados, zera offsets e faz replay paginado dos tipos consumidos. Confirmações e conclusões são preservadas (não reconstruíveis de eventos públicos) e seus contadores permanecem.

Desvínculo e LGPD

Nenhum payload de evento carrega cidadao_id. A confirmação e a conclusão são públicas só como total. A evidência anexada por terceiro é pública com o mesmo aviso e consentimento de mídia pública da captura (art. 11, I). A coordenada de presença do confirmador é privada: não entra em payload nem em projeção. A conclusão segue o mesmo padrão. A partir da terceira validação, a identidade exigida é a conta Google verificada; o vínculo do device anônimo à conta (cidadão.vinculado) funde as validações do mesmo titular, com deduplicação, e as linhas migradas passam a contar como verificadas. O identificador e a presença continuam restritos ao schema d12.

Técnicas e referências de implementação

  • Normalização de texto: lowercase, remoção de acentos, pontuação e stopwords PT mínima; tokens com unigrams + bigrams deduplicados.
  • Similaridade: Jaccard sobre conjuntos de tokens. Haversine própria para distância.
  • Parâmetros estáticos em d12.constants.ts: raio de candidatura 500 m, raio de presença 700 m, similaridade mínima 0,6, limiar de 3 confirmações, limiar de 3 conclusões (CONCLUSAO_CONFIRMACOES_MINIMAS), top 5 candidatas, confirmações e conclusões anônimas até 2 (CONFIRMACOES_ANONIMAS_PERMITIDAS e CONCLUSAO_ANONIMAS_PERMITIDAS) e ao menos uma validação verificada no limiar (VERIFICADAS_MINIMAS_PARA_TRIGGER).
  • Fase 2: embeddings multilíngues leves + busca ANN (FAISS/Annoy). O limiar por categoria passa a se calibrar com duplicidades confirmadas ou descartadas.

Critério de saída

Demandas equivalentes da mesma unidade cívica são sinalizadas como candidatas antes de entrarem no ranking. Com 3 confirmações de cidadãos distintos, sendo ao menos uma verificada, o agregado existe sob um representante: membros com status agregada, fora do mapa, do ranking e do backlog, preservados com timeline e evidências próprias. Com 3 conclusões de cidadãos distintos, sendo ao menos uma verificada, a demanda encerra por via coletiva: sem conselheiro ativo, conclui direto; com conselheiro sorteado, a D-6b registra pendência e a rota de ratificação encerra o ciclo. Validações anônimas valem até o total 2; a terceira exige conta Google verificada.


Fase 3 no desenho original, antecipada para a Fase 1 junto do ciclo de demandas. A antecipação está registrada no Apêndice C.

Contexto e responsabilidade

Acumula o caminho real de resolução de cada ciclo concluído e transforma os casos em conhecimento agregado por município e subcategoria. O perfil do par (município, subcategoria) reúne o que funcionou antes: canais, órgãos, documentos, gargalos, prazo mediano e taxa de resolução. O dossiê explica os casos que compõem o perfil, com referências e datas.

O conhecimento volta ao conselheiro, que vê o caminho dos casos anteriores ao abrir um novo acompanhamento, e ao público, que vê o mesmo dossiê no acompanhamento e no relatório. A décima primeira demanda de buraco na via nasce com o caminho das dez anteriores explicado e concatenado.

Não decide, não prioriza e não prescreve. O dossiê é histórico automático e versionado pelo método caminho_v1. A decisão continua humana, e o conselheiro pode registrar caminhos diferentes, que entram no acúmulo.

Eventos consumidos

  • demanda.georreferenciada — município no campo municipio_id (nível 4), com fallback pelo quarto elo da cadeia_ucs.
  • demanda.categorizada — subcategoria, com fallback para a categoria.
  • conselheiro.atualização_publicada — campos estruturados do caminho: canal, órgão, protocolo, documentos, gargalos e prazos.
  • duplicidade.agregada — marca de membro de agregado, para o membro não contar como caso.
  • demanda.concluída — fecho sem conselheiro ativo.
  • conselheiro.ciclo_concluído — desfecho, prazos, total de atualizações e resumo final.

Eventos produzidos

  • caminho.atualizado (1.0.0) — payload: municipio_id, subcategoria_id, categoria_id, total_casos, prazo_mediano_dias, canais, orgaos, gargalos, documentos, dossie, versao_metodo e gerado_em. Publicado apenas quando o perfil muda. Consumido pela D-7, que projeta d7.caminhos e o dossiê no snapshot da demanda. O workspace do conselheiro lê o dossiê pela projeção da D-7.

Estado próprio (schema d24)

  • d24.demandas: projeção de cada demanda com município, categoria, subcategoria e a marca agregado_representante_id. A coluna da marca não estava no contrato original e entrou na migration 0009_d24.
  • d24.caminhos_casos: caminho de cada caso concluído, com orgao_acionado, canal, protocolo, documentos, gargalos, prazo_dias, desfecho, total_atualizacoes e resumo. desfecho usa concluida ou encerrada_sem_conclusao.
  • d24.caminhos_perfis: perfil agregado por (município, subcategoria), com total_casos, canais, orgaos, documentos, gargalos, prazo_mediano_dias, taxa_resolucao, primeiro e último caso, dossie e versao_metodo.
  • d24.eventos_processados e d24.consumer_offset: protocolo de consumo com replay e idempotência por event_id.

Lógica

consome um dos seis tipos de entrada
→ atualiza d24.demandas ou extrai o caminho para d24.caminhos_casos
(canal, órgão, protocolo, documentos, gargalos e prazo por tipo de atualização)
→ no fecho, grava desfecho, prazo, total de atualizações e resumo
→ membro de agregado não gera caso e o fecho dele é ignorado
→ recomputa o perfil do par (município, subcategoria), com fallback para a categoria
→ publica caminho.atualizado quando o perfil muda

Dossiê caminho_v1

Template determinístico com a contagem de casos, o período, os canais e órgãos mais usados, os documentos e gargalos recorrentes e a lista de referências dos casos. O texto exibe até 20 referências e informa quantas ficaram de fora; a lista completa fica no JSONB dossie. O teto é de 4000 caracteres e o mínimo para exibir é um caso. A sanitização de PII roda nos campos de texto antes de compor o dossiê, e o texto final mantém os identificadores das referências intactos.

Endpoint administrativo

  • POST /api/d24/rebuild (1/hora, PapelOperadorGuard) — reprocessa os eventos consumidos em ordem de sequence_number e reconstrói casos e perfis. O rebuild zera projeções, idempotência e offsets e bloqueia o consumo ao vivo durante a reconstrução.

Técnicas e referências de implementação

  • Agregação determinística por par (município, subcategoria) em SQL, sem embeddings nesta leva. A busca semântica segue a mesma evolução da D-12, na fase da infraestrutura de IA dedicada.
  • Fallback de subcategoria para a categoria quando o caso não tem subcategoria ou não há perfil do par.
  • O dossiê é publicado com publicarComRetry. Falha persistente vai para a DLQ e a retentativa do boot recomputa e republica, sem perda silenciosa.

Critério de saída

Cada ciclo concluído alimenta o perfil do par (município, subcategoria) e o dossiê fica disponível para o conselheiro e para o público. O caminho acumulado não decide nada e é sempre marcado como histórico automático.


Objetivo: escala territorial e robustez institucional. Começa quando ciclos completos foram documentados em múltiplos municípios e os dados da Fase 1 permitem comparação entre unidades cívicas.


Contexto e responsabilidade

Gera demandas de manutenção preventiva a partir de projetos concluídos. Quando uma demanda do tipo “projeto” ou “obra” é concluída com parâmetros de manutenção definidos, esta colônia agenda ciclos conforme o plano registrado pelo responsável técnico. No intervalo programado, publica uma nova demanda que entra no pipeline normal.

Não executa manutenção, não fiscaliza o estado físico do ativo e não decide prioridade. Apenas gera a demanda no momento certo e mantém o vínculo com o projeto original.

Eventos consumidos

  • demanda.concluída (somente demandas com campo plano_manutencao preenchido)
  • manutenção.executada (resultado de uma manutenção concluída, para registro no histórico)
  • demanda.recebida (tracking de pipeline: apenas demandas com origem = 'manutenção_programada')

Eventos produzidos

  • demanda.recebida — a demanda de manutenção em si, com origem = 'manutenção_programada', projeto_original_id e o dossiê.
  • demanda.manutenção_gerada — payload: demanda_id (nova), projeto_original_id, categoria, localizacao, tipo_manutencao, intervalo_programado, prazo_tolerancia e ciclo_numero.
  • manutenção.ciclo_encerrado — quando o projeto atinge o fim da vida útil programada ou o último ciclo previsto.

Estado próprio

  • Projetos monitorados: projeto_id, categoria, localizacao, parâmetros de manutenção (intervalo, tipo, prazo de tolerância), próximo ciclo agendado.
  • Histórico de manutenções por projeto: data, demanda gerada, status, conselheiro responsável.

Lógica

demanda.concluída recebido
→ verifica se a demanda contém plano_manutencao
→ se não contém: ignora
→ se contém: registra no estado próprio com próximo ciclo = data_conclusão + intervalo
varredura periódica (CronJob de polling):
→ seleciona os projetos com proximo_ciclo <= NOW()
→ gera nova demanda com categoria e localizacao herdadas do projeto original
→ publica demanda.recebida e demanda.manutenção_gerada
→ recalcula próximo ciclo (data_atual + intervalo)
→ se próximo ciclo excede a vida útil programada: publica manutenção.ciclo_encerrado

Técnicas e referências de implementação

  • @nestjs/schedule (ScheduleModule + CronJob), com varredura periódica em vez de agendamento dinâmico por projeto.
  • O plano de manutenção é um campo opcional no evento demanda.concluída: plano_manutencao: { intervalo_dias, tipo, prazo_tolerancia_dias, vida_util_dias } | null.
  • A demanda gerada reutiliza o pipeline existente: é uma demanda.recebida como qualquer outra, com origem = 'manutenção_programada' e projeto_original_id.

Fase: 2

Critério de saída

O sistema gera automaticamente demandas de manutenção nos intervalos programados. Cada manutenção tem rastro vinculado ao projeto original. Projetos sem manutenção acumulam tempo de inação visível no backlog.


Contexto e responsabilidade

Verifica se o cidadão tem vínculo legítimo com a unidade cívica em que atua. Vínculo é moradia, emprego ou outro vínculo documentado. O resultado é um status binário: válido ou inválido. Nada mais é armazenado.

Não armazena dados pessoais além do mínimo. Não decide sobre o conteúdo das demandas. Não bloqueia participação — sinaliza para outras colônias o status do vínculo.

Eventos consumidos

  • cidadão.solicitou_validação_vínculo
  • validação.documento_submetido
  • validação.social_concluída (resultado de validação por pares)

Eventos produzidos

  • vínculo.validado — payload: cidadao_id, unidade_civica_id, status (valido/invalido/provisorio), metodo_validacao, data_validade e hash_evidencia (hash do documento, sem o documento em si).
  • validação.social_solicitada — aciona sorteio de validadores pares para confirmação presencial.

Estado próprio

Status de vínculo por cidadao_id + unidade_civica_id. Validade com data de expiração. Hash da evidência (não o documento).

Métodos de validação (em ordem de prioridade)

1. Documental automático
→ título de eleitor, comprovante de residência, contracheque
→ IA extrai campos relevantes, valida formato e consistência
→ cidadão mantém o documento, sistema guarda apenas o hash
2. Validação social por pares
→ sorteio de N cidadãos com vínculo já validado na mesma UC
→ validadores confirmam que conhecem o cidadão ou verificam presença
→ maioria qualificada (2/3) define resultado
3. Presença verificada
→ cidadão realiza ação geolocada dentro do território da UC
→ múltiplas presenças em dias distintos consolidam o vínculo
→ GPS + timestamp do dispositivo como evidência

Lógica

cidadão solicita validação de vínculo em UC X
→ seleciona método preferido
→ se documental:
→ IA processa documento, extrai endereço/vínculo
→ valida consistência dos campos extraídos
→ calcula hash do documento original
→ se confiança suficiente: publica vínculo.validado(provisório)
→ agenda revisão por amostragem (N% dos vínculos provisórios passam por revisão humana)
→ se social:
→ sorteia N validadores elegíveis na UC X
→ publica validação.social_solicitada
→ aguarda resultado
→ se 2/3 confirmam: publica vínculo.validado(válido)
→ define data_validade (parametrizável, referência: 24 meses)

Privacidade por design

O sistema registra apenas: status, método, data/validade e hash da evidência. Não armazena o documento. Não mantém cópia do comprovante. O hash permite verificar que uma evidência foi processada sem expor seu conteúdo. Dados pessoais além do mínimo estritamente necessário não entram no sistema.

Técnicas e referências de implementação

  • OCR para extração de campos de documentos: Tesseract (open source) com modelos pt-BR, ou AWS Textract / Google Document AI como referências cloud.
  • Validação de consistência: regex + heurísticas para campos de endereço, CEP, datas.
  • Hash de evidência: SHA-256 do arquivo original antes de qualquer processamento. O arquivo é descartado após hash calculado e campos extraídos.
  • Revalidação periódica: vínculo expira. Cidadão recebe notificação antes da expiração para renovar.

Critério de saída

Todo cidadão com vínculo válido tem status registrado com data de validade, método e hash de evidência. Nenhum dado pessoal além do mínimo está armazenado. O status é consultável por outras colônias sem acesso aos dados de validação.


Contexto e responsabilidade

Implementa os mecanismos de votação do sistema: abertura e fechamento de rodadas, controle de quórum, registro de resultado com dissenso documentado. A votação é mecanismo pontual de legitimação, não o centro do sistema.

Não decide o que vai a voto. Não decide o resultado além do apurado. Não interfere no ranking nem na execução diretamente — publica o resultado e outras colônias reagem.

Tipos de votação suportados

priorização → re-ranking de demandas dentro de um nível
validação → confirmação de que uma agenda pode avançar
contestação → sinalização de revisão ou interrupção
ajuste_parâmetro → revisão de pesos e regras do sistema
legislativa → deliberação sobre normas (escopo regional/nacional)

Eventos consumidos

  • votação.aberta (publicado pelo sistema de parametrização ou por iniciativa da UC)
  • voto.submetido
  • votação.prazo_encerrado

Eventos produzidos

  • votação.resultado_publicado — payload: votacao_id, tipo, resultado_aprovado (bool), contagem, quórum_atingido, dissenso registrado (contagem dos votos vencidos), versão dos parâmetros vigentes.
  • votação.quórum_não_atingido — encerra rodada sem resultado vinculante.

Estado próprio

Rodadas de votação com status (aberta/encerrada). Votos por cidadao_id e votacao_id (sem expor voto individual — apenas se participou e o resultado agregado). Histórico de resultados.

Lógica de apuração

votação.prazo_encerrado recebido
→ conta votos por opção
→ verifica quórum mínimo (parametrizável por tipo de votação)
→ se quórum não atingido: publica votação.quórum_não_atingido
→ se quórum atingido:
→ calcula resultado (maioria simples, maioria qualificada — por tipo)
→ registra dissenso (contagem dos votos na(s) opção(ões) vencida(s))
→ publica votação.resultado_publicado

Privacidade do voto

O sistema registra que cidadao_id participou da votação (para controle de quórum e prevenção de duplo voto), mas não expõe qual opção escolheu. O resultado publicado é sempre agregado. O dissenso é registrado como contagem, nunca como lista de quem votou no quê.

Técnicas e referências de implementação

  • Prevenção de duplo voto: bloom filter ou conjunto de cidadao_id que já votaram, por votacao_id. Não expõe a lista.
  • Quórum parametrizável: valor mínimo de participantes ou percentual da UC. Diferente por tipo de votação.
  • Votação assíncrona: cidadão pode votar a qualquer momento dentro da janela. Sem necessidade de presença simultânea.
  • Auditabilidade: o resultado agregado, a contagem por opção e o quórum são públicos. A lista de participantes (sem voto) também é pública para verificação de quórum.

Critério de saída

Toda rodada encerrada tem resultado publicado com contagem, quórum, dissenso e versão dos parâmetros vigentes. Nenhum voto individual é exposto.


Contexto e responsabilidade

Monitora padrões anômalos no comportamento de inserção e validação de demandas. Não pune automaticamente. Sinaliza. A decisão de investigar ou atuar é sempre humana.

Não bloqueia demandas por conta própria. Não acessa dados de outras colônias diretamente — consome eventos do barramento.

Eventos consumidos

  • demanda.recebida (monitora padrões de inserção)
  • validação.social_concluída (monitora padrões de co-validação)
  • cidadão.dispositivo_registrado (detecta múltiplas identidades no mesmo dispositivo)

Eventos produzidos

  • anomalia.detectada — payload: tipo_anomalia, entidades envolvidas, score de suspeição, evidências (padrões detectados), ação_recomendada (sempre humana).
  • demanda.score_validade_atualizado — score público de confiabilidade de cada demanda.

Estado próprio

Score de validade por demanda. Histórico de padrões por cidadao_id. Grafo de co-validações.

Tipos de anomalia monitorados (Fase 2)

burst_inserção → N demandas do mesmo cidadão em janela curta
co_validação seletiva → contas novas validando apenas contas novas
múltiplas identidades → mesmo dispositivo com múltiplos cidadão_id
demandas_clonadas → texto muito similar enviado em massa

Lógica de detecção de burst

para cada demanda.recebida de cidadão_id X:
→ conta demandas de X na janela de tempo T (referência: 1 hora)
→ se count > limiar_N: incrementa score de suspeição de X
→ se score > limiar_alerta: publica anomalia.detectada(burst_inserção)
→ demandas de X entram em status "revisão_pendente" até score normalizar

Lógica de grafo de co-validações

para cada validação.social_concluída:
→ registra aresta (validador → validado) no grafo
→ calcula métricas do grafo por janela de tempo:
→ densidade de conexões entre contas novas
→ componentes fortemente conectados com baixa diversidade territorial
→ ratio validações_recebidas / validações_dadas por conta
→ se padrão suspeito detectado: publica anomalia.detectada(co_validação seletiva)

Score de validade da demanda

score_validade = f(
idade_da_conta_do_criador,
histórico_de_criação_do_criador,
número_de_validações_recebidas,
diversidade_dos_validadores,
ausência_de_anomalias_associadas
)

Score é público e aparece na timeline da demanda. Não é eliminatório automaticamente — é sinalização.

Técnicas e referências de implementação

  • Grafo de co-validações: Neo4j ou implementação em memória com NetworkX (Python) para MVP. Algoritmos: PageRank para detectar validadores centrais, detecção de comunidades (Louvain ou Label Propagation).
  • Séries temporais de inserção: sliding window counter por cidadao_id. Redis com estruturas de janela deslizante.
  • Fingerprint de dispositivo: hash de atributos do dispositivo (user-agent, resolução, timezone). Não identifica o usuário, apenas detecta reuso do mesmo dispositivo.
  • Transparência radical como dissuasão: todo histórico de inserção e validação é público. A exposição pública do comportamento é o principal mecanismo de dissuasão — mais eficaz que bloqueio automatizado.

Critério de saída

Demandas com padrão suspeito têm score de validade baixo visível na timeline. Anomalias detectadas geram alerta para revisão humana, não bloqueio automático.


Contexto e responsabilidade

Adiciona ao processamento de demandas uma camada de avaliação de urgência, impacto potencial e criticidade territorial. Demandas de risco elevado recebem peso adicional no ranking e podem acionar notificações específicas.

Não define a prioridade final — complementa o ranking com um fator de urgência quando parâmetros públicos indicam criticidade.

Eventos consumidos

  • demanda.categorizada
  • demanda.georreferenciada

Eventos produzidos

  • demanda.risco_classificado — payload: demanda_id, score_urgencia, score_impacto, score_criticidade_territorial, nivel_risco (baixo/médio/alto/crítico), fatores que contribuíram.

Estado próprio

Score de risco por demanda com versão dos parâmetros usados no cálculo.

Dimensões de risco

urgência → há risco imediato à saúde ou segurança?
(ex: esgoto a céu aberto, estrutura em colapso, ausência de água)
impacto potencial → quantas pessoas são afetadas?
(estimado pela densidade populacional da UC + abrangência territorial)
criticidade_territorial → a UC já tem demandas críticas não resolvidas no mesmo nível?
(acumular risco não resolvido aumenta criticidade das novas)

Lógica

consome demanda.categorizada + demanda.georreferenciada
→ recupera parâmetros de risco por categoria
→ calcula score_urgência com base em palavras-chave de risco + categoria
→ estima população afetada pela área territorial da UC
→ verifica histórico de demandas críticas não resolvidas na mesma UC/categoria
→ calcula score composto
→ classifica em nível de risco
→ publica demanda.risco_classificado
→ se nível crítico: aciona notificação para conselheiros e gestores da UC

Técnicas e referências de implementação

  • Detecção de urgência por texto: dicionário de termos de risco por categoria + score de presença. Complementado por classificador de urgência treinável.
  • Estimativa de população: dados do IBGE por setor censitário. Cruzamento geoespacial com a área da demanda.
  • Criticidade acumulada: série temporal de demandas críticas não resolvidas por UC e categoria. Integra com o histórico da D-7 transparência.

Critério de saída

Toda demanda categorizada tem score de risco calculado. Demandas críticas geram notificação imediata. O nível de risco é visível na timeline pública.


Contexto e responsabilidade

Integra os dados entre os níveis de unidade cívica. Demandas que excedem a capacidade do nível local escalam com contexto completo para o nível superior. Produz visões agregadas sem perder o rastro das demandas individuais.

Não decide quais demandas escalam — aplica critérios parametrizados de capacidade por nível.

Eventos consumidos

  • demanda.ranqueada (monitora acúmulo por nível de UC)
  • ranking.atualizado (reavaliação do acúmulo quando o ranking de uma UC muda de forma significativa)
  • agenda.item_disponível (monitora fila sem conselheiros disponíveis por período)
  • conselheiro.ciclo_concluído (atualiza capacidade disponível por UC)

Eventos produzidos

  • demanda.escalada — payload: demanda_id, unidade_civica_origem, unidade_civica_destino, motivo_escalonamento, contexto completo da demanda.
  • agregado.municipal_atualizado — visão consolidada das demandas de todos os bairros de um município.
  • agregado.regional_atualizado — visão consolidada de múltiplos municípios.

Estado próprio

Visões agregadas por nível com histórico temporal. Rastro de escalonamentos por demanda_id.

Critérios de escalonamento

capacidade_técnica → demanda requer expertise ou recursos indisponíveis no nível atual
capacidade_financeira → custo estimado excede orçamento disponível no nível atual
abrangência_territorial → impacto ultrapassa os limites da UC atual
fila_sem_conselheiro → demanda sem conselheiro disponível por período > limiar parametrizado

Lógica

para demanda em unidade_cívica X de nível N:
→ avalia critérios de escalonamento
→ se algum critério atingido:
→ identifica UC de nível N+1 responsável pelo território de X
→ empacota contexto completo (histórico, validações, status, evidências)
→ publica demanda.escalada para UC nível N+1
→ mantém referência na UC original (visibilidade bidirecional)
→ atualiza agregados dos níveis superiores

Técnicas e referências de implementação

  • Hierarquia territorial: estrutura de árvore com UC de cada nível contendo referências às UCs filhas. Consultas de nível N+1 são sempre eficientes.
  • Agregados pré-computados: atualizados por evento, não calculados on-demand. Redis ou materialized views no PostgreSQL.
  • Bidirecionalidade: a UC original mantém visibilidade da demanda escalada. O cidadão que a registrou pode acompanhar pela timeline mesmo após o escalonamento.

Critério de saída

Demandas escaladas chegam ao nível superior com contexto completo e rastreável. Visões agregadas por município e região estão disponíveis em tempo próximo ao real.


Contexto e responsabilidade

Expõe os dados do sistema para uso acadêmico, jornalístico e de auditoria externa. Dados anonimizados, agregados, com schema documentado e versionado.

Não expõe dados pessoais. Não decide o que é público — aplica as regras de privacidade definidas nos parâmetros do sistema.

Eventos consumidos

  • ranking.atualizado, agenda.gerada, votação.resultado_publicado e outros eventos de estado do sistema.

Eventos produzidos

  • dataset.exportado — publicado quando um novo snapshot de dataset é gerado.

Estado próprio

Snapshots versionados de datasets por data e versão de schema.

Datasets disponíveis (referência)

demandas_anonimizadas → categoria, nível, UC, status, timestamps (sem cidadão_id)
rankings_históricos → score, posição, versão dos parâmetros — por UC e data
agenda_histórica → itens, status de conclusão, tempo médio por categoria
conselheiros_histórico → ciclos (sem identificação pessoal), tipos de demanda, tempo
votações → resultados, quórum, tipo, UC
cobertura_territorial → índice de cobertura por UC por período

Lógica

publicação periódica (ex: diária ou semanal):
→ coleta estado atual das projeções de leitura
→ aplica regras de anonimização (k-anonimidade mínima por agregado)
→ gera arquivo em formato aberto (CSV, Parquet, JSON Lines)
→ calcula hash do arquivo
→ publica em repositório público com versão e hash
→ publica dataset.exportado

Técnicas e referências de implementação

  • Formatos: CSV para acessibilidade, Parquet para análise em grande escala, JSON Lines para streaming.
  • k-anonimidade: grupos com menos de k registros são suprimidos ou generalizados (ex: UC com poucas demandas tem dados agrupados no nível superior).
  • Versionamento: nomenclatura por data + hash. Ex: demandas_2026-03-14_sha256-abc123.parquet.
  • Repositório público: GitHub Releases ou equivalente para datasets pequenos. IPFS ou sistema de armazenamento distribuído para escala.

Critério de saída

Datasets públicos disponíveis para download em formato aberto, com schema documentado, versionados por data. Nenhum dado pessoal exposto.


Contexto e responsabilidade

Implementa o encadeamento de hashes sobre os eventos do barramento. Qualquer alteração retroativa no histórico de eventos se torna detectável por verificação independente, sem depender da infraestrutura do sistema.

Não processa conteúdo de negócio. Não interfere no fluxo de eventos. Apenas registra e publica checkpoints verificáveis.

Eventos consumidos

  • Todos os eventos do barramento (apenas para fins de hashing — não processa payload de negócio).

Eventos produzidos

  • hash.checkpoint_publicado — payload: bloco_id, hash do bloco atual, hash do bloco anterior (encadeamento), número de eventos no bloco, timestamp, hash raiz de Merkle dos eventos do bloco.

Estado próprio

Cadeia de checkpoints com hash encadeado. Último hash conhecido para continuação da cadeia.

Lógica

para cada N eventos processados (ou a cada intervalo de tempo):
→ coleta os N eventos em ordem de sequence number
→ calcula hash de cada evento (SHA-256 do payload serializado)
→ constrói Merkle tree com os N hashes
→ calcula hash do bloco = SHA-256(hash_raiz_merkle + hash_bloco_anterior + timestamp)
→ persiste bloco no estado próprio
→ publica hash.checkpoint_publicado

Verificação independente

Qualquer pessoa pode, dado o histórico de eventos do barramento e a cadeia de checkpoints publicados:

  1. Recalcular o hash de cada evento.
  2. Reconstruir a Merkle tree de cada bloco.
  3. Verificar se o hash do bloco confere com o publicado.
  4. Verificar se o encadeamento é contínuo (hash anterior de cada bloco aponta para o bloco anterior).

Qualquer alteração retroativa quebra a cadeia a partir do ponto modificado.

Publicação de checkpoints

Os checkpoints são publicados em repositório externo ao sistema (ex: GitHub, IPFS, sistema de notarização pública). Isso garante que mesmo que o sistema seja comprometido, os checkpoints históricos sejam imutáveis em repositório independente.

Técnicas e referências de implementação

  • Hash: SHA-256. Padrão, amplamente auditável.
  • Merkle tree: implementação direta ou biblioteca. Referência: bitcoin-merkle-tree pattern.
  • Notarização externa: OpenTimestamps para ancorar checkpoints em blockchain pública (Bitcoin) sem custo por transação.
  • Frequência dos blocos: parâmetro configurável. Referência: a cada 1000 eventos ou a cada hora, o que vier primeiro.

Critério de saída

A cadeia de checkpoints é verificável de forma independente. Qualquer alteração retroativa é detectável. Checkpoints publicados em repositório externo ao sistema.


Contexto e responsabilidade

Registra o histórico de atuação de cada conselheiro, aplica critérios de elegibilidade para progressão entre níveis e bloqueia reentrada indevida.

Não faz o sorteio. A D-6a sorteia; esta colônia diz quem pode ou não participar do sorteio.

Eventos consumidos

  • conselheiro.ciclo_concluído
  • conselheiro.avaliação_registrada
  • conselheiro.sorteado (para registrar início de ciclo)
  • capacitação.concluída

Eventos produzidos

  • conselheiro.elegibilidade_atualizada — publicado sempre que o status de elegibilidade muda.
  • progressão.habilitada — conselheiro completou os requisitos para sortear no nível seguinte.

Estado próprio

Histórico de ciclos por conselheiro_id: nível, unidade cívica, demandas acompanhadas, avaliações, capacitações concluídas. Status de elegibilidade atual por nível.

Critérios de progressão

para nível N+1:
→ concluiu ao menos 1 ciclo completo no nível N
→ ciclo concluído = cargo do início ao fim + ao menos 1 demanda acompanhada + sem registro negativo pendente
→ manifestou interesse no nível N+1
→ completou cursos obrigatórios para o nível N+1
→ passou em prova de habilitação (quando exigida)
→ está adimplente com o sistema

Lógica

conselheiro.ciclo_concluído recebido
→ registra ciclo no histórico
→ verifica se todos os critérios de progressão para nível N+1 estão atendidos
→ se sim: publica progressão.habilitada
→ atualiza elegibilidade para sorteios futuros
→ publica conselheiro.elegibilidade_atualizada

Bloqueio de reentrada indevida

tentativa de sorteio no nível N sem ciclo N-1 concluído → bloqueada, notificação ao candidato
tentativa de sorteio com registro negativo pendente → colocada em fila de revisão
conselheiro com múltiplas recusas de atribuição → elegibilidade suspensa temporariamente

Técnicas e referências de implementação

  • Máquina de estados por conselheiro: estados possíveis por nível (não_elegível, elegível, em_ciclo, ciclo_concluído, progressão_habilitada, suspenso). Transições geradas por eventos.
  • Histórico imutável: cada ciclo é um registro append-only. Nada é apagado ou alterado retroativamente.
  • Auditabilidade pública: o histórico de ciclos é público no nível de “ciclos concluídos por nível” sem expor dados pessoais do conselheiro além do necessário.

Critério de saída

A elegibilidade de qualquer conselheiro para qualquer nível é calculável a partir do histórico de eventos. Nenhuma reentrada indevida é possível sem que o critério de ciclo anterior esteja registrado.


Objetivo: sistema resistente a manipulação e preparado para pressão institucional de longo prazo. Começa após ciclos completos em escala regional e primeiras tentativas reais de captura respondidas.


Contexto e responsabilidade

Permite projetar o efeito de alterações nos parâmetros de priorização antes de aplicá-las ao sistema em produção. Opera sobre cópia versionada do estado atual. Não altera nenhum dado de produção.

Eventos consumidos

  • simulação.solicitada (por comitê técnico ou pesquisador)
  • simulação.parâmetros_alternativos_submetidos

Eventos produzidos

  • simulação.resultado_publicado — payload: simulacao_id, parâmetros usados, ranking resultante por UC, diferença em relação ao ranking atual, metodologia.

Estado próprio

Snapshots de simulações com parâmetros e resultados. Histórico de simulações realizadas.

Lógica

simulação.solicitada recebida
→ cria cópia do estado atual do ranking e parâmetros
→ aplica parâmetros alternativos propostos sobre os dados históricos
→ recalcula ranking para cada UC com os novos parâmetros
→ calcula delta: quais demandas subiriam, quais desceriam, em quantas posições
→ gera relatório comparativo
→ publica simulação.resultado_publicado (sempre com metodologia e parâmetros usados)

Técnicas e referências de implementação

  • Análise de sensibilidade: variação de cada parâmetro ±N% e comparação do efeito no ranking. Identifica quais parâmetros têm maior influência no resultado.
  • Sandbox de dados: banco de leitura separado para simulações. Jamais conectado ao barramento de produção.
  • Visualização comparativa: heatmap de posições antes/depois por UC. Útil para comitês técnicos avaliarem impacto territorial de mudanças de parâmetro.

Critério de saída

Qualquer proposta de mudança de parâmetro acompanha simulação com resultado publicado. O efeito da mudança é quantificado antes de qualquer votação.


Contexto e responsabilidade

Formaliza o ciclo completo de revisão de parâmetros: proposta documentada, simulação de impacto, votação em camadas por perfil, publicação do resultado com dissenso, versionamento da mudança.

Nenhum parâmetro muda sem que o histórico da decisão seja público e rastreável.

Eventos consumidos

  • parâmetro.proposta_submetida
  • simulação.resultado_publicado (requisito para avançar no ciclo)
  • votação.resultado_publicado (cada camada de votação)
  • parâmetro.aprovado (todas as camadas concluídas)

Eventos produzidos

  • parâmetros.atualizados — payload: novo conjunto de parâmetros, versão, histórico da decisão (proposta, simulações, resultados de votação por camada, dissenso).

Estado próprio

Catálogo de versões de parâmetros com histórico completo de cada mudança.

Fluxo do ciclo de revisão

1. proposta documentada submetida por comitê técnico
2. simulação obrigatória de impacto (D-18)
3. votação em 4 camadas:
→ pesquisadores: método
→ técnicos (secretarias, autarquias): aplicabilidade
→ cidadãos: peso relativo percebido
→ conselhos de UC: ajuste local
4. resultado de cada camada registrado com dissenso
5. parâmetro aprovado entra em vigor com nova versão
6. parâmetros.atualizados publicado no barramento
→ colônia de ranking recalcula scores com novos parâmetros

Critério de saída

Qualquer versão de parâmetros é rastreável até a proposta que a originou, com simulações e resultados de votação por camada preservados. A fórmula de priorização não muda silenciosamente.


D-20 — Anti-abuso e Resistência a Manipulação Coordenada

Seção intitulada “D-20 — Anti-abuso e Resistência a Manipulação Coordenada”

Contexto e responsabilidade

Expande os mecanismos da Fase 2 (D-11) com detecção de padrões mais sofisticados: coordenação entre grupos geograficamente dispersos, uso de identidades válidas em comportamento coordenado, inserção gradual projetada para passar pelos filtros de burst.

Não pune automaticamente. Qualquer padrão detectado gera alerta público. A decisão de investigar é humana.

Eventos consumidos

  • demanda.recebida, validação.social_concluída, voto.submetido — todos com metadados de contexto.
  • anomalia.detectada (do D-11; esta colônia consome e correlaciona anomalias já detectadas).

Eventos produzidos

  • manipulação.padrão_detectado — payload: tipo de padrão, entidades envolvidas, evidências (séries temporais, grafos de correlação), nível de confiança.

Estado próprio

Grafos de co-validação e co-inserção expandidos. Séries temporais de comportamento por cidadao_id. Histórico de padrões detectados.

Padrões sofisticados monitorados

coordenação_dispersa → cidadãos em UCs distintas inserindo demandas similares
em horários correlacionados (sugere organização central)
identidades_válidas_coordenadas → cidadãos com histórico limpo exibindo comportamento
sincronizado em janela curta (voto/validação)
inserção_gradual → volume de inserção abaixo do limiar de burst,
mas com crescimento consistente e conteúdo muito similar
captura_de_parâmetro → concentração de participação de um grupo específico
nas votações de ajuste de parâmetro

Lógica de detecção de coordenação dispersa

para cada janela de tempo W:
→ agrupa demandas com embedding similar (cosine > 0.9)
→ verifica distribuição geográfica das UCs de origem
→ calcula correlação temporal das inserções
→ se alta similaridade + dispersão geográfica + correlação temporal elevada:
→ publica manipulação.padrão_detectado(coordenação_dispersa)

Técnicas e referências de implementação

  • Análise de grafos: algoritmos de detecção de comunidades (Louvain, Label Propagation). Detecção de comportamento coordenado: Coordinated Inauthentic Behavior (CIB) detection — técnicas usadas em análise de desinformação em redes sociais.
  • Séries temporais: DTW (Dynamic Time Warping) para detectar padrões temporais similares entre grupos distintos.
  • Cross-correlação de embeddings + tempo: combina similaridade semântica com correlação temporal para detectar campanhas coordenadas.
  • Alerta público: qualquer padrão detectado é publicado com evidências. Não há detecção silenciosa.

Critério de saída

Padrões de manipulação coordenada são detectados e alertados publicamente antes de afetarem o ranking. A decisão de ação é sempre humana e rastreável.


Contexto e responsabilidade

Produz visões consolidadas do desempenho dos ministérios e secretarias responsáveis pela execução das agendas. Compara prazos prometidos com realizados, rastreia desvios de escopo e custo, agrega indicadores por categoria.

Eventos consumidos

  • conselheiro.atualização_publicada (contém atualizações de andamento da execução)
  • agenda.item_concluído
  • ministério.plano_submetido (custo estimado, prazo, cronograma)

Eventos produzidos

  • desempenho.executivo_atualizado — payload: ministerio_id, indicadores de desempenho por categoria, desvios identificados.

Estado próprio

Histórico de desempenho por ministério/secretaria e categoria de demanda.

Indicadores produzidos

prazo_realizado vs. prometido → por agenda, por categoria, por executor
desvio_de_custo → custo realizado vs. estimado
taxa_de_conclusão_no_prazo → % agendas encerradas dentro do prazo
gargalos_recorrentes_por_executor → tipos de entrave mais frequentes por ministério
velocidade_de_resposta → tempo entre protocolo e primeira resposta do executivo

Técnicas e referências de implementação

  • Painel público: dashboard com filtros por período, ministério, categoria de demanda e UC. Todas as métricas com fonte rastreável no histórico de eventos.
  • Alertas de desvio: quando prazo prometido é ultrapassado sem atualização, alerta público é gerado automaticamente.

Critério de saída

O painel de desempenho executivo é público. Qualquer comparação entre prazo prometido e realizado é rastreável até o evento que gerou cada dado.


Contexto e responsabilidade

Módulo isolado que processa o histórico de eventos do sistema para gerar indicadores derivados: tendências de demanda por categoria, velocidade de execução, padrões de gargalo recorrente. Opera como leitura independente. Não interfere em nenhum fluxo de produção.

As projeções são publicadas com metodologia documentada e distinguidas com clareza de dados factuais.

Eventos consumidos

  • Replay do histórico completo do barramento (acesso somente leitura ao log de eventos).

Eventos produzidos

  • projeção.publicada — payload: tipo de projeção, metodologia, período analisado, resultado, intervalo de confiança, hash do dataset usado.

Estado próprio

Histórico de projeções com versão do dataset e metodologia.

Tipos de projeções

tendência_de_demanda_por_categoria → crescimento/decrescimento de volume por categoria/UC
velocidade_de_execução_por_tipo → tempo médio histórico por tipo de agenda
gargalos_estruturais → padrões de entrave recorrentes por executor/território
cobertura_territorial_projetada → estimativa de quando uma UC atingirá cobertura mínima
impacto_de_mudança_de_parâmetro → projeção do efeito de parâmetros alternativos (via simulação)

Transparência das projeções

Toda projeção é publicada com:

  • Metodologia completa (qual algoritmo, quais dados, qual período).
  • Dataset usado (referência ao snapshot versionado do D-15).
  • Intervalo de confiança ou margem de erro.
  • Distinção explícita: “isso é projeção baseada em dados históricos, não é dado factual”.

Pesquisadores externos podem replicar qualquer projeção a partir dos datasets públicos.

Critério de saída

Projeções publicadas são replicáveis externamente. A distinção entre dado factual e projeção é sempre explícita. Nenhuma projeção interfere no fluxo de produção.


Contexto e responsabilidade

Gera periodicamente um estado completo e auditável do sistema: ranking atual, parâmetros vigentes, agenda ativa, histórico de mandatos, métricas de cobertura territorial. Assinado criptograficamente e publicado em repositório aberto.

Funciona como evidência imutável do estado do sistema em qualquer momento — útil para auditorias externas, pesquisa longitudinal e disputas sobre o histórico.

Eventos consumidos

  • snapshot.geração_solicitada (publicado em schedule periódico)

Eventos produzidos

  • snapshot.publicado — payload: snapshot_id, data de geração, hash do arquivo, URL do repositório externo.

Estado próprio

Histórico de snapshots com hash e URL de acesso.

Conteúdo do snapshot

ranking_atual → posição e score de cada demanda ativa por UC (anonimizado)
parâmetros_vigentes → versão e valores de todos os parâmetros ativos
agenda_ativa → backlog atual com status de cada item
histórico_de_mandatos → ciclos concluídos por nível (sem identificação pessoal)
cobertura_territorial → índice de cobertura por UC
métricas_de_sistema → volume de eventos, taxa de conclusão, tempo médio por categoria

Lógica

snapshot.geração_solicitada recebido
→ coleta estado atual de todas as projeções de leitura
→ aplica anonimização (consistente com o D-15)
→ serializa em formato aberto (JSON + Parquet)
→ calcula hash do conjunto de arquivos
→ assina criptograficamente com chave da vanguarda do software
→ publica no repositório externo (GitHub, IPFS ou equivalente)
→ registra no estado próprio
→ publica snapshot.publicado

Critério de saída

Snapshots gerados periodicamente, assinados e publicados em repositório externo. Verificáveis por hash independentemente da infraestrutura do sistema.


Módulos paralelos ao fluxo principal das unidades cívicas. Não interferem no ranking de demandas nem no ciclo de conselheiros. Operam sobre os dados das organizações.


Fase 1 — MVP

Contexto e responsabilidade

Registra e mantém o perfil de cada organização: razão social, porte, setor, localização e associação às unidades cívicas do território de atuação. É o ponto de entrada para todas as colônias de empresa. Sem registro, não há visibilidade no sistema.

No MVP, o cadastro é manual — preenchido diretamente pela empresa ou por um representante. A adesão começa pelas empresas entusiastas: pequenas e médias que querem fazer parte do modelo de transparência. Não há integração automática com bases externas de CNPJ nesta fase.

Eventos consumidos

  • lugar.georreferenciado — quando o lugar associado à sede ou filial da organização é resolvido pela Colônia L-2, a E-1 consome esse evento para definir a associação territorial. A localização de uma organização é dado das colônias de lugares, não da E-1.

Eventos produzidos

  • empresa.cadastrada
  • empresa.associação_territorial_definida — vincula a empresa às UCs do território de operação.
  • lugar.recebido — um por endereço de operação, com tipo_lugar = 'organizacao', publicado diretamente no barramento. O correlacao_id de cada endereço volta no lugar.georreferenciado e permite o lookup reverso.

Estado próprio

Cadastro de organizações com status (cadastrada/socializada/pendente/recusada).

Lógica de associação territorial

empresa cadastrada com endereço(s) de operação
→ para cada endereço: publica lugar.recebido(tipo=organizacao) via barramento
→ aguarda lugar.georreferenciado correspondente da Colônia L-2
→ quando recebido: extrai unidade_civica_id e cadeia de UCs pai
→ define UCs primárias (sede) e secundárias (filiais, área de impacto)
→ publica empresa.associação_territorial_definida

A E-1 não chama a D-2 de demandas diretamente. O georreferenciamento de organizações passa pela L-2, que além de resolver o território enriquece a tipificação com bases de CNPJ e OSM — dado útil para o cadastro institucional.

Simplificações válidas no MVP

  • Cadastro via formulário manual. Sem integração com Receita Federal ou base pública de CNPJ.
  • Associação territorial: a empresa informa os endereços de operação e a L-2 resolve o georreferenciamento. Endereços sem coordenadas ou com publicação falha ficam com status_geo = 'pendente' e são republicados pela varredura de órfãos do boot.
  • Porte declarado pela empresa: micro, pequena, média, grande. Sem validação cruzada com faturamento nesta fase.

Critério de saída

Toda empresa cadastrada tem associação territorial definida e visível. O cadastro é público no nível de razão social, setor e território — dados financeiros ficam nas colônias específicas.

Documento detalhado: E-1 - Cadastro Institucional.md


Fase 1 — MVP

Contexto e responsabilidade

Recebe e processa a autodeclaração da folha salarial da empresa. Aplica a razão máxima salarial parametrizada (referência: 10x). Calcula o que mudaria na folha sem aumentar o custo total.

Esta é a colônia central do MVP de empresas. Ela responde a pergunta concreta: dado o que a empresa declara, qual é a razão atual entre o menor e o maior salário, e o que seria necessário redistribuir para chegar ao parâmetro de referência? O resultado é o “achatamento simulado” — quanto o maior salário precisaria ceder e quanto isso representa no bolso de quem está na base.

Não decide sobre a empresa. Não aplica automaticamente mudanças. Produz o diagnóstico.

Eventos consumidos

  • empresa.cadastrada — mantém projeção local de empresas ativas (e2.empresas_projecao), via UPSERT idempotente.

Eventos produzidos

  • empresa.folha_submetida — publicado pela face BFF da E-2 quando a empresa submete a folha. event_id = submissao_id.
  • empresa.diagnóstico_salarial_publicado — payload: empresa_id, menor salário, maior salário, razão atual, razão máxima parametrizada, redistribuição necessária sem aumento de custo total, custo total antes e depois. event_id = diagnostico_id.

Face BFF

A E-2 tem BFF acoplado para o formulário de folha e a leitura do diagnóstico:

  • POST /api/empresas/:id/folha — folha em JSON (cargos + salários, sem nomes). Autenticado por JWT.
  • POST /api/empresas/:id/folha/csv — folha em CSV multipart (cabeçalho cargo,salario[,jornada], delimitador vírgula ou ponto-e-vírgula, formatos brasileiros).
  • A submissão é idempotente por hash de conteúdo (duplicata retorna 409 permanente), sem janela de tempo.
  • Os dois eventos (folha_submetida e diagnóstico_salarial_publicado) são publicados no mesmo fluxo síncrono do controller, com retry inline.

Estado próprio

Diagnóstico salarial por empresa com versão dos parâmetros usados. Submissões brutas preservadas com idempotencia_hash e payload_diagnostico para auditoria.

Lógica

empresa submete folha via POST (JSON ou CSV)
→ valida cargos e salários (sem nomes), idempotência por hash
→ publica empresa.folha_submetida
→ recupera lista de salários declarados
→ calcula razão maior/menor
→ se razão > parâmetro_máximo (10x no MVP):
→ calcula redistribuição interna por compressão logarítmica (ln → comprime → exp, com aritmética decimal):
→ comprime os gaps entre cargos proporcionalmente
→ preserva a ordem relativa e mantém o custo total da folha constante
→ gera diagnóstico com before/after
→ publica empresa.diagnóstico_salarial_publicado

Transparência do diagnóstico

O diagnóstico é público no nível de agregados: “razão atual foi X, parâmetro é Y, redistribuição equivale a Z% do total da folha”. Salários individuais não são expostos publicamente — apenas internamente para o conselho da empresa e auditores autorizados.

Simplificações válidas no MVP

  • Folha declarada via duas rotas: JSON estruturado com campos por cargo e salário, ou CSV multipart (parser interno; papaparse/csv-parse não constam nos pacotes permitidos). Sem OCR, sem extração automática de holerite.
  • A lista de salários é enviada sem nomes — apenas cargo e valor. A E-2 não armazena identificação de colaborador.
  • Cálculo da redistribuição é determinístico, por compressão logarítmica: os gaps entre cargos são comprimidos proporcionalmente, sem teto rígido, e o custo total da folha permanece constante ao centavo.
  • Parâmetro inicial fixo em configuração (razão 10x). Sem sistema de parametrização dinâmica nesta fase.

Critério de saída MVP

Empresa com folha declarada tem diagnóstico publicado com: razão atual calculada, razão parametrizada, valor total da folha antes e depois da redistribuição, e a contagem de colaboradores que cederam e que receberam. Nenhum salário individual exposto.

Documento detalhado: E-2 - Transparência Salarial e Folha.md


Fase 1 — MVP

Contexto e responsabilidade

Recebe os dados declarados pela empresa (folha, receita, excedente) e aplica os parâmetros públicos para calcular o excedente disponível para redistribuição e a projeção dos fluxos possíveis.

Nenhum cálculo é sigiloso. Metodologia pública. Qualquer pessoa pode replicar a simulação com os mesmos dados de entrada.

No MVP, a simulação opera sobre dados autodeclarados sem verificação cruzada. O valor está na visibilidade: a empresa que declara abre a caixa-preta e demonstra o que seria possível. Cada simulação publicada é um dado público — comparável com outras empresas do mesmo porte e setor, agregável para análises territoriais.

Eventos consumidos

  • empresa.cadastrada (mantém projeção local de empresas ativas)
  • empresa.folha_submetida (extrai total_colaboradores para o cálculo do valor por trabalhador)
  • empresa.diagnóstico_salarial_publicado (extrai custo_total_depois — folha reequilibrada — para a fórmula do excedente)
  • empresa.balanço_submetido (gatilho principal: recebe receita, custos e referência à folha)
  • parâmetros.atualizados (stub no MVP — recalcula simulações com novos parâmetros na Fase 2)

Face BFF

A face BFF da E-3 recebe a submissão de balanço e persiste em e3.balancos_submissoes antes de publicar empresa.balanço_submetido, com idempotência por hash de conteúdo (duplicata retorna 409 permanente). POST /api/empresas/:id/balanco responde 202 Accepted com { submissao_id, status: 'aceito' }. Submissões com evento_publicado_em nulo são republicadas na varredura do boot. Balanço sem periodo_referencia é descartado com log. A leitura pública das simulações é GET /api/empresas/:id/simulacoes, que espelha o payload do evento sem os campos internos.

Documento detalhado com justificativa completa de cada evento, contratos e algoritmos: E-3 - Simulação Econômica.md

Eventos produzidos

  • empresa.balanço_submetido — publicado pela face BFF no POST de balanço. event_id = submissao_id.
  • empresa.simulação_econômica_publicada — payload: empresa_id, receita declarada, custo declarado, excedente calculado, divisão parametrizada (valores iniciais de referência: 40% caixa interno, 40% retorno ao sistema, 20% trabalhadores), total_colaboradores, valor_por_trabalhador, período de referência, identificador do diagnóstico e da folha base. Sem rateio por UC: o retorno ao sistema é um fluxo único para o fomento.

Lógica

A E-3 mantém projeções locais atualizadas por evento:
→ empresa.cadastrada: projeção de empresas ativas (id, razao_social, porte, setor_id, status, representante_id)
→ empresa.folha_submetida + empresa.diagnóstico_salarial_publicado:
projeção unificada de folha (total_colaboradores + custo_total_depois)
empresa.balanço_submetido recebido
→ verifica se a folha referenciada já tem diagnóstico:
se não: armazena como pendente, processa quando diagnóstico chegar
→ obtém custo_total_depois e total_colaboradores da projeção de folha
→ calcula excedente = receita - custos operacionais - folha_reequilibrada
→ aplica divisão parametrizada:
→ X% caixa interno (solvência e reinvestimento)
→ Y% retorno ao sistema (fluxo único para o fomento)
→ Z% distribuição igualitária entre trabalhadores
→ calcula valor por trabalhador na parte de distribuição
→ publica empresa.simulação_econômica_publicada

Simplificações válidas no MVP

  • Balanço declarado via formulário (face BFF na própria E-3): receita bruta, custos operacionais, folha (já declarada na E-2). O excedente é calculado pela colônia — não exige DRE completo.
  • Sem verificação cruzada com Receita Federal ou SPED. A declaração é pública e verificável socialmente. O próprio sistema de transparência radical é o mecanismo de pressão por veracidade.
  • Parâmetros de divisão (40/40/20) fixos em configuração no MVP, como ponto de partida a calibrar. Variantes paramétricas ficam na Fase 2 com o sistema de governança de parâmetros.
  • Cálculos monetários com precisão de centavos via aritmética decimal (decimal.js), não ponto flutuante nativo.
  • O retorno ao sistema é um fluxo único, sem rateio por UC. A associação territorial da empresa não participa do cálculo no MVP. O cruzamento com o território entra na E-4 (Fase 2).
  • Projeções locais de estado de outras colônias (empresa.cadastrada, empresa.folha_submetida, empresa.diagnóstico_salarial_publicado) mantidas via consumo de eventos, sem consulta direta a bancos externos.
  • Balanços que chegam antes do diagnóstico salarial da folha referenciada são armazenados como pendentes e processados automaticamente quando o diagnóstico é publicado. Pendentes são varridos no boot e por intervalo periódico, sem depender de evento novo. Sem polling ou retry pelo usuário.

Critério de saída MVP

A simulação de qualquer empresa cadastrada é pública e replicável com os dados declarados. O resultado mostra: excedente calculado, valor do caixa interno, valor de retorno ao sistema e valor de distribuição por trabalhador. A metodologia e os parâmetros usados estão visíveis junto ao resultado.

Documento detalhado: E-3 - Simulação Econômica.md


Contexto e responsabilidade

Cruza a redistribuição simulada pela colônia econômica com as demandas mapeadas nas unidades cívicas do território de atuação da empresa. Produz uma projeção concreta do que o excedente poderia financiar.

Esta projeção não é compromisso nem garantia. É evidência quantificada do potencial de impacto.

Eventos consumidos

  • empresa.simulação_econômica_publicada
  • ranking.atualizado (para saber quais são as demandas prioritárias das UCs do território)

Eventos produzidos

  • empresa.impacto_territorial_publicado — payload: empresa_id, UC(s) de impacto, demandas prioritárias que poderiam ser financiadas, custo estimado de cada, cobertura do excedente disponível.

Lógica

empresa.simulação_econômica_publicada recebida
→ recupera UCs associadas à empresa
→ recupera top-N demandas prioritárias de cada UC (do ranking)
→ recupera custo estimado de cada demanda (quando disponível)
→ ordena demandas por prioridade
→ simula quanto do excedente que retorna ao fomento cobre cada demanda do território da empresa
→ gera projeção: "com X reais de excedente, seria possível financiar as demandas A, B e C da UC Y"
→ publica empresa.impacto_territorial_publicado

Critério de saída

Para cada empresa com simulação econômica publicada, existe uma projeção concreta de impacto territorial baseada nas demandas reais da UC. A projeção é pública e rastreável.


Contexto e responsabilidade

Processa a autodeclaração de funções por cargo: cargo formal, salário, descrição das atividades reais. Gera um mapa de cargos consistente com a estrutura declarada.

Não expõe salários individuais publicamente. O mapa derivado é público no nível de estrutura e cargo. Serve como base para onboarding replicável e para o relatório do conselho interno.

Eventos consumidos

  • empresa.estrutura_submetida

Eventos produzidos

  • empresa.mapa_estrutural_publicado — payload: empresa_id, mapa de cargos agregado (sem nomes), sobreposições detectadas, recomendações de redistribuição de função.

Lógica

empresa.estrutura_submetida recebida
→ agrupa declarações por cargo formal
→ compara descrições de atividades reais vs. atribuições formais do cargo
→ detecta sobreposição: mesma atividade declarada por múltiplos cargos distintos
→ detecta lacuna: atividade sem cargo formal associado
→ gera mapa agregado de cargos com funções reais
→ publica empresa.mapa_estrutural_publicado

Técnicas e referências de implementação

  • Clustering semântico de descrições de atividades: embeddings + DBSCAN para agrupar atividades similares independente do cargo formal declarado.
  • Comparação com CBO (Classificação Brasileira de Ocupações): validação das funções declaradas contra descrições oficiais de cargos.

Módulos paralelos ao fluxo de demandas e ao fluxo de empresas. Operam sobre a camada de realidade territorial: o que existe fisicamente no território, onde está, de que tipo é e em que quantidade.

A separação de responsabilidades em relação às colônias de empresa é deliberada: a colônia de empresa sabe o que a organização faz, quanto produz e quanto distribui. A colônia de lugar sabe onde ela está e o que existe ao redor. A localização de uma organização é dado das colônias de lugares, consumido pelas colônias de empresa via evento — nunca escrito diretamente por elas.

Fases de implementação:

  • L-1 e L-2 — Fase 1: cadastro e georreferenciamento são simples e complementam o ciclo de captura desde o início. Entusiastas já podem popular o mapa de realidade territorial junto com as primeiras demandas.
  • L-3 — subconjunto no MVP; Fase 2 completa: o MVP entrega a confirmação automática, a confirmação social por presença, a denúncia, a retirada pelo autor e a reativação por operador. Reincidência temporal, validação presencial sorteada e revalidação periódica ficam para a Fase 2, quando houver volume mínimo de dados.
  • L-4 e L-5 — Fase 2: análise de cobertura e incubação social requerem volume mínimo de dados para funcionar com significância.

Contexto e responsabilidade

Ponto de entrada do dado de lugar no sistema. Recebe o evento lugar.recebido publicado pela D-1a (fluxo do cidadão) ou pela E-1 (endereço de organização), valida os campos mínimos obrigatórios, gera o lugar_id definitivo e persiste o dado bruto. Consome também lugar.georreferenciado da L-2 para atualizar o status do registro sem que outra colônia escreva em seu banco.

Não valida conteúdo, não georeferencia, não verifica se o lugar existe de fato. Apenas aceita, registra e publica. O dado bruto nunca é descartado.

Eventos consumidos

  • lugar.recebido — publicado pela D-1a (cidadão) ou pela E-1 (endereço de organização).
  • lugar.georreferenciado — publicado pela L-2 após resolver UC e tipificação. A L-1 atualiza o próprio status do registro para georreferenciado ao consumir este evento.

Eventos produzidos

  • lugar.cadastrado (1.2.0) — payload: lugar_id, tipo_lugar (residencia, organizacao, equipamento_publico ou poligono_uc), subtipo, nome, coordenadas brutas, cidadao_id, timestamp, canal de entrada, horario_funcionamento, descricao (opcional, máximo de 5000 caracteres, publicado quando informado; alimenta o painel de detalhes do lugar no mapa e a projeção da D-7, e entra na allowlist de redação como conteúdo público do lugar) e alerta_proximidade (opcional, quando detectada duplicata por proximidade).

Estado próprio (write model)

Registro de lugares com status pendente_georreferenciamento. Dado bruto preservado com referência ao lugar_id. A L-1 é a única colônia que escreve neste schema. A atualização de status ao consumir lugar.georreferenciado fecha o ciclo sem violação de isolamento.

Lógica

lugar.recebido chega
→ valida campos mínimos (tipo_lugar + coordenadas)
→ gera lugar_id único (UUID v4)
→ deduplica preliminarmente por proximidade (< 10 metros, mesmo tipo): se detectada, inclui alerta_proximidade no payload como metadado, sem bloquear
→ persiste dado bruto no estado próprio com status pendente_georreferenciamento
→ publica lugar.cadastrado
lugar.georreferenciado chega
→ busca registro por lugar_id
→ atualiza status para georreferenciado

Técnicas e referências de implementação

  • Validação de coordenadas: bounding box do território coberto. Rejeita coordenadas fora da área de operação.
  • Idempotência: por event_id na camada de barramento. O lugar_id é UUID v4 gerado pela L-1 — identidade independente do conteúdo. Submissões duplicadas do barramento (mesmo event_id) são detectadas e ignoradas.
  • Deduplicação preliminar: Haversine em raio de 10 metros com pré-filtro por bounding box. Detecta lugares do mesmo tipo muito próximos e inclui o metadado alerta_proximidade no payload de lugar.cadastrado. Não bloqueia: a L-3 decide sobre a existência de fato do lugar.

Critério de saída

Qualquer input válido de lugar gera um lugar_id rastreável e o evento lugar.cadastrado no barramento. O dado bruto original está preservado e acessível. O status é atualizado exclusivamente pela L-1 ao consumir o evento da L-2.

Documento detalhado: L-1 - Cadastro de Lugares.md


Colônia L-2 — Georreferenciamento e Tipificação

Seção intitulada “Colônia L-2 — Georreferenciamento e Tipificação”

Contexto e responsabilidade

Resolve a questão territorial do lugar (a qual UC pertence) e valida a coerência da tipificação (o subtipo declarado é plausível para aquelas coordenadas e contexto). Análoga à D-2 do fluxo de demandas, com a responsabilidade adicional de enriquecer e confirmar o tipo declarado.

É esta colônia que publica lugar.georreferenciado — o evento que a Colônia E-1 (Cadastro Institucional) consome para definir a associação territorial de uma organização às suas UCs. Nenhuma colônia de empresa acessa a D-2 de demandas para isso.

Não valida se o lugar existe. Não aciona validação social. Apenas resolve geometria e tipificação.

Eventos consumidos

  • lugar.cadastrado

Eventos produzidos

  • lugar.georreferenciado — payload: lugar_id, unidade_civica_id de menor nível resolvida, nivel_minimo_resolvido, cadeia_ucs (lista de UCs pai, do menor ao maior nível), metodo_resolucao (gps, endereco ou inferencia; no MVP sempre gps), confianca_geo, subtipo_declarado?, subtipo_confirmado?, confianca_tipificacao (alta, media, baixa ou nao_avaliada) e enriquecimento (dados adicionais de bases abertas quando disponíveis).

Estado próprio

Cache de resoluções geo por coordenada de lugar. Referências de enriquecimento por lugar_id.

Lógica

lugar.cadastrado recebido
→ point-in-polygon contra base de polígonos das UCs (mesma base da D-2, Turf.js)
→ resolve nível mínimo e cadeia completa de UCs pai
→ calcula confianca_geo: sempre `alta` no MVP (toda coordenada chega por GPS; `media`/`baixa` variam na Fase 2 com o geocoding)
→ tenta enriquecimento de tipificação (MVP: somente OSM Overpass API):
→ cruzamento com OpenStreetMap por coordenada e raio, com cache em memória
→ se OSM indisponível: degrada com graça para `nao_avaliada` e segue o fluxo
→ se enriquecimento confirma o subtipo declarado: confianca_tipificacao = alta
→ se enriquecimento sugere subtipo diferente: registra divergência, mantém declarado como principal
→ registra resolução no cache
→ publica lugar.georreferenciado

CNPJ (Receita Federal) e CNES/Inep para equipamentos públicos entram na Fase 2. No MVP, organizações são tipificadas somente pelo OSM.

Sub-colônias potenciais (se o volume escalar)

  • Resolvedor territorial: point-in-polygon puro, mesmo da D-2.
  • Enriquecedor de tipificação: cruzamento com OSM, CNPJ, CNES, Inep.

Técnicas e referências de implementação

  • Point-in-polygon: Turf.js em aplicação (mesma stack da D-2). Sem PostGIS no MVP.
  • Cache de coordenadas: Map em memória no MVP. Redis com TTL longo entra na Fase 2. Coordenadas de lugar mudam muito menos que demandas.
  • Enriquecimento OSM: Overpass API para consultas por bounding box. Cache agressivo em memória por coordenada.
  • Consulta CNPJ: API pública da Receita Federal (dados.gov.br) ou base local espelhada. Busca por coordenada aproximada e razão social quando disponível.
  • Bases de equipamentos públicos: CNES (saúde), Censo Escolar/Inep (educação), base de equipamentos IBGE. Todas abertas e atualizáveis periodicamente.
  • Confiança composta: confianca_geo × confianca_tipificacao geram um score único que a L-3 usa como ponto de partida da validação.

Critério de saída

Todo lugar cadastrado tem unidade_civica_id resolvida, cadeia de UCs pai completa e tipificação com nível de confiança registrado. Lugares com confiança baixa são sinalizados para a L-3 com prioridade de validação.

Documento detalhado: L-2 - Georreferenciamento e Tipificação.md


Fase 2 no modelo completo; subconjunto social e automático no MVP

Contexto e responsabilidade

Verifica se o lugar existe de fato e se os dados estão corretos. No modelo completo, opera por múltiplos métodos em sequência de custo crescente: cruzamento automático com bases abertas, confirmação temporal por múltiplos cidadãos e validação social presencial sorteada.

O MVP implementa um subconjunto: a confirmação automática pela tipificação da L-2, a confirmação social por presença (3 confirmações de cidadãos distintos em 700 m), a denúncia com motivo em lista fechada (3 denúncias levam o lugar a disputado), a retirada pelo autor e a reativação por operador. A reincidência temporal, a validação presencial sorteada, a revalidação periódica de 24 meses e o evento lugar.validação_social_solicitada ficam para a Fase 2.

Não valida demandas. Não analisa cobertura. Só determina se um lugar é confiável o suficiente para entrar no mapa como confirmado.

Eventos consumidos

  • lugar.cadastrado — no MVP, cria o registro provisório com autor e coordenadas.
  • lugar.georreferenciado — no MVP, aplica a confirmação automática quando a confiança é alta.
  • lugar.validação_social_concluída (resultado de confirmação por cidadãos sorteados) — Fase 2.
  • lugar.atualização_submetida (cidadão corrige dado de lugar existente) — Fase 2.

Eventos produzidos

  • lugar.validado — payload: lugar_id, status_confianca (provisorio/confirmado/disputado), metodo_validacao (opcional; automatico, social ou operador), total_confirmacoes, total_denuncias e versao_dados. Publicado a cada confirmação e denúncia aceitas.
  • lugar.validação_social_solicitada — aciona sorteio de validadores presenciais quando necessário. Fase 2.
  • lugar.desativado — payload: lugar_id e motivo (denuncias/retirado_pelo_autor/operador). No MVP, apenas a retirada pelo autor publica.

Estado próprio

Status de confiança por lugar_id com histórico de validações e métodos usados. No MVP, o schema l3 tem quatro tabelas: status_lugares, confirmacoes, denuncias e consumer_offset. A transição grava publicacao_pendente na mesma atualização e a varredura do boot republica a saída com o event_id determinístico quando a publicação falha. O histórico de atualizações por lugar_id fica para a Fase 2.

Métodos de validação (em ordem de custo crescente)

1. Automático por enriquecimento
→ se confianca_geo = alta e confianca_tipificacao = alta sem divergência:
status = confirmado com metodo_validacao = automatico
(implementado no MVP)
2. Confirmação social por presença
→ 3 confirmações de cidadãos distintos, autor excluído, presença ≤ 700 m
→ status = confirmado com metodo_validacao = social
→ 3 denúncias de cidadãos distintos: status = disputado
→ retirada pelo autor: status = desativado
→ reativação por operador: status = provisorio
(implementado no MVP)
3. Reincidência temporal e validação presencial sorteada
→ N cidadãos cadastram o mesmo lugar em janela de tempo
→ sorteio auditável de cidadãos com vínculo validado na UC
→ confirmação com foto atual + geolocalização no raio do lugar
→ se 2/3 confirmam: status = confirmado
→ se maioria nega: status = disputado → revisão manual
(Fase 2)

Lógica

lugar.cadastrado recebido
→ registra o lugar como provisório com autor e coordenadas
lugar.georreferenciado recebido
→ avalia o método 1 (enriquecimento automático)
→ se confiança alta nas duas dimensões e sem divergência: publica lugar.validado(confirmado)
→ senão: mantém provisório
confirmação social recebida
→ valida autor, presença de 700 m e repetição
→ incrementa o total; ao atingir 3, confirma com método social
→ publica lugar.validado
denúncia recebida
→ valida autor, presença de 700 m, motivo e repetição
→ incrementa o total; ao atingir 3, disputa com método social
→ publica lugar.validado
retirada do autor
→ desativa e publica lugar.desativado
reativação por operador
→ volta a provisório e publica lugar.validado
boot
→ republica publicações pendentes de lugar.validado e lugar.desativado com o event_id determinístico

Técnicas e referências de implementação

  • Agrupamento por proximidade: DBSCAN com Haversine para detectar cadastros do mesmo lugar por cidadãos distintos. Fase 2.
  • Limiar de confiança automática: no MVP, confianca_geo = alta e confianca_tipificacao = alta sem divergência. O score composto parametrizado fica para a Fase 2.
  • Sorteio de validadores: mesmo padrão da D-6a — seed público, Fisher-Yates, auditável. Fase 2.
  • Foto de validação: hash SHA-256 + metadados EXIF (coordenadas, timestamp). Processada pela D-1c de anexos. Fase 2.
  • Revalidação periódica: lugares confirmados passam por reverificação a cada 24 meses ou quando N cidadãos reportam mudança. Fase 2.
  • Presença: Haversine em memória, no mesmo helper compartilhado da D-12. Sem PostGIS no MVP.

Critério de saída

Todo lugar tem status de confiança público e rastreável. Lugares provisórios ficam visíveis no mapa com marcação distinta. A taxa de lugares em status provisório por mais de 90 dias é indicador de cobertura insuficiente na UC.

Documento detalhado: L-3 - Validação e Qualidade.md


Fase 2

Contexto e responsabilidade

Cruza o mapa de lugares validados com parâmetros técnicos de cobertura mínima por tipo de organização e UC. Produz o mapa de gaps: onde falta o quê, em que quantidade, com que evidência.

Não financia, não aciona criação de organizações. Publica o gap documentado — que a Colônia L-5 consome para iniciar o ciclo de incubação.

Eventos consumidos

  • lugar.validado (atualiza o índice de cobertura)
  • lugar.desativado (reduz cobertura quando um lugar some)
  • parâmetros.atualizados (recalcula gaps quando parâmetros de cobertura mudam)
  • demanda.ranqueada (cruza demandas recorrentes de um tipo com ausência de lugar correlato)

Eventos produzidos

  • cobertura.atualizada — payload: uc_id, tipo de organização, quantidade existente, quantidade mínima parametrizada, índice de cobertura (0.0–1.0), timestamp.
  • gap.detectado — payload: gap_id, uc_id, tipo de organização faltante, quantidade faltante, parâmetro de referência usado, demandas correlatas (lista de demanda_id do ranking da UC de categoria relacionada), nivel_prioridade_gap, timestamp.
  • gap.resolvido — publicado quando novo lugar validado fecha um gap existente. Payload: gap_id, lugar_id que resolveu, data de resolução.

Estado próprio

Índice de cobertura por tipo de organização e UC, com histórico temporal. Registro de gaps ativos com data de detecção, nível de prioridade e demandas correlatas. Histórico de gaps resolvidos.

Parâmetros de cobertura (referências iniciais, a calibrar)

Os parâmetros partem de estudos existentes (OMS, Ministério da Saúde, Anvisa, FNDE, IBGE) e são revisados pelos comitês técnicos de parametrização. Exemplos de referência:

farmácia → 1 a cada 5.000 habitantes (referência CFF)
mercado / supermercado → 1 a cada 2.000 domicílios
padaria / panificadora → 1 a cada 3.000 habitantes
UBS → 1 a cada 3.450 habitantes (referência MS / PNAB)
creche pública → cobertura de 50% das crianças de 0–3 anos (meta PNE)
escola fundamental → raio máximo de 1,5km em área urbana (referência FNDE)

Nenhum desses valores está fixo no código. Todos são parâmetros versionados, auditáveis e recalibráveis.

Lógica de gap

lugar.validado recebido (ou parâmetros.atualizados)
→ recupera todos os lugares validados da UC por tipo
→ recupera parâmetro de cobertura mínima para aquele tipo na UC
→ recupera população estimada da UC (IBGE — setor censitário)
→ calcula cobertura_atual = contagem_lugares / meta_calculada
→ atualiza cobertura.atualizada
→ se cobertura_atual < 1.0:
→ calcula quantidade_faltante = ceil(meta - contagem_existente)
→ recupera demandas ativas no ranking da UC de categoria correlata
(ex: ausência de farmácia + demandas 2.1, 2.2, 2.4 recorrentes)
→ calcula nível_prioridade_gap:
→ gap puro (sem demanda correlata): prioridade baixa
→ gap + demandas correlatas presentes: prioridade média
→ gap + demandas correlatas de nível 1 ou 2: prioridade alta
→ publica gap.detectado
→ se lugar resolveu gap existente: publica gap.resolvido

Técnicas e referências de implementação

  • Estimativa populacional por UC: cruzamento com setores censitários do IBGE. Atualização a cada censo ou com dados da Fase 2 de pesquisas recorrentes.
  • Correlação demanda–tipo de lugar: dicionário de mapeamento (categoria_id → subtipo de lugar correlato). Parâmetro público, revisável.
  • Dashboard público de cobertura: mapa de calor por UC e tipo de organização. Endpoint paginado com filtros por tipo, UC e nível de prioridade do gap.
  • Histórico de gaps: cada gap tem linha do tempo pública: quando foi detectado, quais demandas estavam correlatas, quando foi resolvido e por qual lugar.

Critério de saída

O mapa de gaps de qualquer UC é público, atualizado em tempo próximo ao real, com parâmetros de referência visíveis e demandas correlatas linkadas. Qualquer cidadão ou pesquisador pode verificar por que um gap foi detectado.


Fase 2

Contexto e responsabilidade

Consome gaps detectados e demandas mapeadas para estruturar o processo de criação de novas organizações socializadas. É a colônia que converte evidência em ação: conecta o gap documentado a grupos com capacidade de operá-lo e ao caixa das unidades cívicas ou estruturas de fomento.

Não financia diretamente. Não cria a organização. Estrutura o processo, registra o ciclo e publica os eventos que ativam as colônias de empresas quando a nova organização nasce.

Consome exclusivamente o que outras colônias produzem. Não tem interface direta com o cidadão — opera sobre dados já processados.

Eventos consumidos

  • gap.detectado (da L-4 — gatilho principal)
  • gap.resolvido (encerra processos de incubação associados ao gap resolvido)
  • empresa.cadastrada (da E-1 — confirma que a organização incubada foi formalizada)
  • parâmetros.atualizados (ajusta critérios de elegibilidade de propostas)

Eventos produzidos

  • incubação.processo_aberto — payload: incubacao_id, gap_id, tipo de organização, UC, nível de prioridade, prazo de manifestação de interesse, critérios mínimos de proposta.
  • incubação.proposta_registrada — payload: incubacao_id, proposta_id, grupo proponente, capacidade técnica declarada, modelo de operação proposto.
  • incubação.proposta_aprovada — payload: incubacao_id, proposta_id, fonte de financiamento vinculada, marcos acordados.
  • incubação.concluída — payload: incubacao_id, empresa_id da organização criada, data de início de operação.
  • incubação.cancelada — quando gap é resolvido por outra via ou proposta não atinge critérios mínimos.

Estado próprio

Registro de processos de incubação com status (aberto/em_avaliação/aprovado/concluído/cancelado). Histórico de propostas por incubacao_id. Marcos e prazos acordados.

Lógica

gap.detectado recebido com nível_prioridade >= médio
→ abre processo de incubação
→ publica incubação.processo_aberto (visível publicamente na UC)
→ cidadãos ou grupos manifestam interesse via interface (proposta simples)
→ para cada proposta recebida:
→ registra declaração de capacidade técnica e modelo proposto
→ publica incubação.proposta_registrada
→ comissão de avaliação da UC (sorteada, critérios parametrizados):
→ avalia viabilidade técnica e aderência ao modelo socializado
→ se aprovada: vincula fonte de financiamento (caixa da UC ou fomento)
→ publica incubação.proposta_aprovada
→ organização é criada e cadastrada nas colônias de lugares e empresas
→ quando empresa.cadastrada recebida:
→ registra conclusão
→ publica incubação.concluída

O modelo socializado como requisito de entrada

Toda organização nascida de um processo de incubação entra no modelo socializado desde o início: transparência de folha, razão salarial parametrizada, excedente com destino rastreável para o fomento do sistema. Não é opcional. É critério de aprovação da proposta.

Técnicas e referências de implementação

  • Comissão de avaliação: sorteio pelo mesmo mecanismo da D-6a. Mandato limitado ao ciclo de avaliação.
  • Critérios de proposta: parametrizados e públicos. Incluem: capacidade técnica mínima declarada, modelo de governança interna, projeção de cobertura do gap, prazo de início de operação.
  • Matching de financiamento: colônia consulta o caixa disponível do fomento do sistema via evento. Não acessa diretamente nenhum saldo — publica requisição e aguarda confirmação.
  • Transparência do processo: toda proposta, avaliação e decisão é pública. O processo de incubação tem timeline pública análoga à timeline de demandas.

Critério de saída

Todo gap de prioridade média ou alta com processo de incubação aberto tem timeline pública rastreável. Organizações criadas via incubação estão linkadas ao gap que as originou — o ciclo completo (gap detectado → organização operando → gap resolvido) é verificável por qualquer cidadão.


Camada Transversal — Dados Brutos para Informação

Seção intitulada “Camada Transversal — Dados Brutos para Informação”

Esta seção não é uma colônia. É a descrição do pipeline que perpassa todas as colônias: como o dado bruto que entra pelo sistema de ingestão se transforma em informação útil, agregável e auditável.


DADO BRUTO
texto livre + localização aproximada + foto + horário
NORMALIZAÇÃO (D-1b)
campos estruturados + coordenadas validadas
CLASSIFICAÇÃO (D-3 + D-13)
categoria + nível + score_horizontal + score_risco
PONTUAÇÃO (D-4)
score_final = peso_nacional × peso_situacional × score_horizontal
AGREGAÇÃO (D-5 + D-14)
backlog por UC / visão multinível / dashboards comparativos
PROJEÇÃO (D-22)
tendências / gargalos / velocidade / cobertura
EXPORTAÇÃO (D-15 + D-23)
datasets públicos / snapshots assinados / repositório aberto

Cada transformação é um evento. Cada evento é rastreável. O dado bruto original nunca é descartado. A versão normalizada, categorizada e pontuada é derivada — não substituta.


Colônia Função da IA Limite explícito
D-1b Normalização Transcrição de áudio, descrição de imagem, tradução da legenda para o português, estruturação de texto e denylist de termos Não publica sem confirmação do cidadão. A denylist apenas sinaliza para a moderação.
D-1c Anexos Classificação de imagem: NSFW, detecção de pessoa e heurística de documento/PII NSFW bloqueia por limiar. Pessoa apenas sinaliza. A revisão humana é reativa.
D-1d Moderação Não usa IA própria. Organiza a fila da sinalização da D-1b e da D-1c. A decisão é humana. A IA não aprova nem bloqueia conteúdo.
D-3 Categorização Classificação automática com score de confiança Acima do limiar automático: publica com marcação e caminho de contestação. Abaixo do limiar mínimo: bloqueia até revisão humana.
D-6b Relatoria Sugestão de estruturação de atualizações Rotina: conselheiro revisa e confirma. Sensível: revisão obrigatória. IA nunca publica sem o conselheiro ter visto a tela.
D-9 Validação de vínculo Extração de campos de documentos Resultado provisório, auditado por amostragem
D-11 Anti-fraude Detecção de padrões anômalos Não bloqueia — apenas sinaliza para revisão humana
D-12 Duplicidade Fase 1: heurística determinística de similaridade textual. Fase 2: embedding + busca semântica Sinaliza candidatas, não mescla. Agregação por confirmação coletiva
D-24 Memória de Caminhos Agregação determinística do caminho e dossiê por template, sem modelo de linguagem Histórico automático com referências. Não prescreve e não decide
D-13 Risco Score de urgência por texto e contexto territorial Não remove da fila automaticamente
D-20 Anti-abuso Detecção de coordenação sofisticada Alerta público, decisão humana
D-22 Projeções Tendências e padrões históricos Sempre marcadas como projeção, nunca como dado factual
E-2 Folha salarial Validação de consistência de campos declarados Não altera dados — apenas sinaliza inconsistência
E-5 Estrutura Clustering semântico de descrições de função Mapa é sugestão, empresa confirma ou corrige

A IA nunca decide, nunca prioriza e nunca publica sem revisão humana. É redução de atrito, não autoridade.


O sistema melhora com o tempo porque cada correção humana alimenta os modelos:

  • Categorização corrigida manualmente → par (texto, categoria_correta) entra no dataset de treino.
  • Vínculo invalidado por revisão → par (documento, resultado_correto) melhora o modelo de OCR.
  • Anomalia confirmada por investigação → padrão vira regra de detecção aprimorada.
  • Duplicidade confirmada ou descartada → ajusta o limiar de similaridade por categoria.

Cada erro detectado e corrigido é dado estruturado. O sistema não esconde erros — documenta e aprende.


Cidadão
→ [D-1a Captura] → demanda.recebida
→ [D-1b Normalização] → demanda.normalizada
→ [D-2 Georreferenciamento] → demanda.georreferenciada
→ [D-3 Categorização] → demanda.categorizada
→ [D-1c Anexos] → anexo.processado (consome demanda.recebida, em paralelo à D-1b)
→ [D-1d Moderação] ← anexo.processado (pendente ou sensível; 1.4.0 com o object_key_temp)
← demanda.normalizada (conteúdo suspeito; 1.4.0 com as midias_descritas)
← conselheiro.atualização_publicada (relato com conteúdo suspeito)
→ d1d.fila_moderacao + d1d.decisoes_moderacao (decisão e histórico na mesma transação)
→ d1d.descricoes_midia (descrição automática de cada imagem, casada ao item pelo object_key_temp)
→ moderacao.decidida → [D-1c Anexos] → anexo.moderado → [D-7 Transparência]
→ anexo.removido → [D-7 Transparência]
→ [D-7 Transparência] libera, oculta ou limpa o texto e o relato
→ [N-0d Direitos do Titular] limpa o texto dos schemas na remoção
→ [D-12 Duplicidade] → duplicidade.candidata_detectada
→ [D-13 Risco] → demanda.risco_classificado
→ [D-4 Priorização] → demanda.ranqueada / ranking.atualizado
→ [D-5 Agenda] → agenda.gerada / agenda.item_disponível
→ [D-6a Sorteio] → conselheiro.sorteado ────┐
│ → conselheiro.atribuicao_recusada ──┐ │
│ ┌───────────────────────────────────┘ │
│ ▼ (re-sorteio interno) │
│ [D-6a] processa recusa, │
│ re-sorteia mesma demanda │
│ │
[BFF D-1a] → conselheiro.atualização_registrada ─────┤
│ │
→ [D-6b Relatoria] → conselheiro.demanda_iniciada
→ conselheiro.atualização_publicada
→ conselheiro.prazo_próximo (CronJob 6h)
→ conselheiro.ciclo_concluído ──┐
→ demanda.concluída ─────┘ │
┌───────────────────────────────┘
▼ (libera conselheiro)
[D-6a] varre fila de pendentes, novo sorteio
[D-5 Agenda] atualiza status (atribuido / em_progresso / concluido)
← [D-7 Transparência] consome todos os eventos acima → timeline pública + dashboard
[D-12 Duplicidade] consome: demanda.recebida, demanda.normalizada, demanda.categorizada,
demanda.georreferenciada, demanda.concluída, conselheiro.sorteado, duplicidade.agregada,
demanda.conclusao_confirmada (índice, quando a ratificação é desnecessária),
cidadão.vinculado (funde relator, confirmações e conclusões do device na conta Google)
→ duplicidade.candidata_detectada / demanda.confirmada / demanda.evidencia_adicionada
→ duplicidade.agregada (3 confirmações de cidadãos distintos, presença ≤ 700 m,
ao menos uma verificada; anônimo no limiar 2 recebe 401 login_necessario)
→ [D-4 Priorização] desativa membros, recalcula posições, republica ranking.atualizado
→ [D-5 Agenda] marca membros como agregado, regenera backlog
→ [D-6a Sorteio] remove os membros da fila de pendentes
[D-12 Conclusão coletiva] → demanda.evidencia_adicionada (via: 'conclusao') por mídia
→ demanda.conclusao_confirmada (3 conclusões de cidadãos distintos, presença ≤ 700 m,
ao menos uma verificada; anônimo no limiar 2 recebe 401 login_necessario;
ratificacao_necessaria = demanda atribuída tem conselheiro sorteado)
→ [D-5 Agenda] conclui item fora de em_progresso com data_conclusao e dias_ate_conclusao;
item em_progresso aguarda ratificação
→ [D-6b Relatoria] pendência de ratificação com acompanhamento ativo;
POST /d6b/demandas/:id/ratificar-conclusao reusa o fluxo de conclusão
→ [D-7 Transparência] snapshot concluida (ou só timeline quando há ratificação);
evidências projetadas com via conclusao
[D-24 Memória de Caminhos] consome: demanda.georreferenciada, demanda.categorizada,
conselheiro.atualização_publicada, duplicidade.agregada, demanda.concluída e
conselheiro.ciclo_concluído
→ caminho.atualizado
→ [D-7 Transparência] projeta d7.caminhos e o dossiê no snapshot da demanda;
o workspace do conselheiro lê o dossiê por essa projeção
[D-11 Anti-fraude] consome: demanda.recebida, validação.social_concluída, cidadão.dispositivo_registrado
[D-14 Agregação] consome: demanda.ranqueada, ranking.atualizado, agenda.item_disponível, conselheiro.ciclo_concluído
[D-17 Mandato] consome: conselheiro.ciclo_concluído, conselheiro.sorteado, conselheiro.avaliação_registrada, capacitação.concluída
Fase 2:
[D-9 Vínculo] consome: cidadão.solicitou_validação_vínculo, validação.documento_submetido, validação.social_concluída
[D-10 Votação] consome: votação.aberta, voto.submetido, votação.prazo_encerrado
[D-15 Pesquisa] consome: eventos de estado agregado
[D-16 Integridade] consome: todos os eventos (apenas para hashing)
Fase 3:
[D-18 Simulação] opera sobre snapshot de parâmetros e ranking
[D-19 Governança de parâmetros] consome: proposta, simulação, votação
[D-20 Anti-abuso] consome: padrões do D-11 + eventos de comportamento
[D-21 Monitoramento] consome: atualizações de conselheiro + planos de ministério
[D-22 Projeções] consome: replay do histórico completo
[D-23 Snapshot] consome: estado de todas as projeções de leitura
Colônias de empresa (paralelas — E-1/E-2/E-3 no MVP, E-4/E-5 na Fase 2):
[E-1] → empresa.cadastrada + lugar.recebido(tipo=organizacao)
→ [L-1/L-2] → lugar.cadastrado → lugar.georreferenciado
→ [E-1] consome lugar.georreferenciado → empresa.associação_territorial_definida
[E-2] consome: empresa.cadastrada (projeção local)
[E-2] face BFF → empresa.folha_submetida → empresa.diagnóstico_salarial_publicado
[E-3] consome: empresa.cadastrada, empresa.folha_submetida, empresa.diagnóstico_salarial_publicado, empresa.balanço_submetido, parâmetros.atualizados
[E-3] face BFF → empresa.balanço_submetido → empresa.simulação_econômica_publicada
→ [E-4] → empresa.impacto_territorial_publicado (Fase 2 — depende de L-4)
[E-5] → empresa.mapa_estrutural_publicado (Fase 2)
Colônias de lugares (paralelas):
[D-1a] → lugar.recebido
→ [L-1] → lugar.cadastrado
→ [L-2] → lugar.georreferenciado
→ [L-3] → lugar.validado / lugar.desativado
→ [D-7] projeta status_confianca, metodo_validacao e total_confirmacoes em d7.lugares_geo
→ [L-3] → lugar.validação_social_solicitada (Fase 2)
→ [L-4] → cobertura.atualizada / gap.detectado / gap.resolvido (Fase 2)
→ [L-5] consome: gap.detectado
→ incubação.processo_aberto
→ incubação.proposta_registrada
→ incubação.proposta_aprovada
→ incubação.concluída → empresa.cadastrada (via E-1)
[E-1] publica lugar.recebido(tipo=organizacao) para a L-1 ao cadastrar organização
[L-4] consome: demanda.ranqueada (cruza gaps com demandas correlatas)
[L-5] consome: empresa.cadastrada (confirma conclusão da incubação)

Das colônias de negócio mais o núcleo, o MVP mínimo que fecha um ciclo completo — demandas, conselheiros, transparência — e inicia o mapeamento de empresas é:

Bloco 1 — Núcleo (pré-requisito absoluto):

Prioridade Colônia Justificativa
1 N-0a (Event Bus) Sem barramento não existe nada
2 N-0b (Registry) Idioma comum, pré-requisito de todas
3 N-0c (Observabilidade) Sem logs, erros são invisíveis

Bloco 2 — Ciclo de demandas (Fase 1):

Prioridade Colônia Justificativa
4 D-1a Captura Ponto de entrada do cidadão
5 D-1b Normalização Dado bruto não serve ao pipeline
6 D-1c Gestão de Anexos Armazenamento de mídia do cidadão
7 D-1d Moderação Fecha a entrada: sinalização automática e decisão humana
8 D-2 Georreferenciamento Sem território, não há UC
9 D-3 Categorização Sem categoria, sem ranking
10 D-12 Detecção de Duplicidade Agrega demandas equivalentes antes do ranking
11 D-4 Priorização e Ranking Sem ranking, sem agenda
12 D-5 Agenda Sem backlog, sem conselheiro
13 D-6a Sorteio e Atribuição Sem sorteio, sem acompanhamento
14 D-6b Relatoria e Acompanhamento Sem relatoria, sem transparência
15 D-7 Transparência Sem timeline pública, o ciclo não é verificável

Bloco 3 — Lugares (Fase 1, paralelo ao ciclo de demandas):

Prioridade Colônia Justificativa
16 L-1 Cadastro de Lugares Entusiastas populam o mapa de realidade territorial desde o início
17 L-2 Georreferenciamento e Tipificação Associação territorial das organizações depende daqui
18 L-3 Validação e Qualidade (subconjunto) Confirmação automática, confirmação social por presença, denúncia e retirada fecham o ciclo de vida do lugar

Bloco 4 — Empresas (Fase 1, paralelo):

Prioridade Colônia Justificativa
19 E-1 Cadastro Institucional Pré-requisito de todas as colônias de empresa
20 E-2 Transparência Salarial e Folha Mapeamento de cargos e diagnóstico do achatamento salarial
21 E-3 Simulação Econômica Simulação do excedente e da redistribuição — responde “quanto mudaria”

Simplificações válidas no MVP:

Ciclo de demandas:

  • D-1c Anexos: MinIO com object key derivada do hash SHA-256 (deduplicação por hash), validação de MIME por magic bytes, EXIF removido via sharp. Sem OCR. Classificação local de NSFW e pessoa com transformers.js.
  • D-1d Moderação: fila única com filtro de tipo, denylist de texto no lugar de modelo de toxicidade e decisão tripla (aprovado, bloqueado ou removido) com confirmação. O histórico append-only por item guarda decisão, motivo, moderador, data e reversão. A remoção física é reservada ao conteúdo ilegal e recusada no relato; a D-1c apaga a mídia e a N-0d limpa o texto.
  • D-2 Georreferenciamento: base de polígonos no schema core (core.uc_polygons) compartilhada com a L-2 via leitura direta. Exceção controlada ao isolamento — justifica-se como dependência de infraestrutura. Migra para colônia dedicada de geo na Fase 2. Confiança geo sempre alta no MVP; ponto fora de todos os polígonos gera revisão pendente.
  • D-3 Categorização: classificador por palavras-chave e regras. Sem modelo de ML. Fila de revisão persistida para confiança < 0.85.
  • D-12 Duplicidade: heurística determinística (mesma UC e categoria, distância de até 500 m, Jaccard de 0,6) e confirmação coletiva. Sinaliza candidatas, não mescla.
  • D-4 Priorização: pesos fixos em configuração. Sem recalculação dinâmica por evento de parâmetro. Ranking em memória com posições persistidas e reconstrução no boot.
  • D-6a Sorteio: seed auditável = HMAC-SHA256 do último event_id do barramento e do timestamp do sorteio. Fisher-Yates determinístico. Sorteio serializado por UC.
  • D-7 Transparência: timeline em texto plano. Dashboard calculado on-demand a partir dos snapshots; rebuild via replay do barramento.

Lugares:

  • L-1: sem deduplicação sofisticada. Alerta simples por proximidade de coordenadas.
  • L-2: enriquecimento via OSM apenas. CNPJ e bases de equipamentos públicos (CNES, Inep) entram na Fase 2. Base de polígonos compartilhada com a D-2 via core.uc_polygons (mesma exceção controlada).
  • L-3: subconjunto social e automático no MVP. Confirmação automática pela confiança da L-2, 3 confirmações de cidadãos distintos em 700 m, 3 denúncias para disputar, retirada pelo autor e reativação por operador. Sem reincidência temporal, sem validação presencial sorteada e sem revalidação periódica.

Empresas:

  • E-1: cadastro manual via formulário. Sem integração automática com base de CNPJ no MVP.
  • E-2: folha declarada via duas rotas (JSON ou CSV multipart), face BFF dentro da colônia. Sem OCR ou extração automática. Diagnóstico calculado sobre os dados declarados.
  • E-3: cálculo sobre dados autodeclarados. Sem verificação cruzada com fontes externas. A simulação é transparente — metodologia pública, replicável com os dados de entrada.

Adiar para Fase 2 (MLP — sem perder o ciclo):

Colônias do ciclo de demandas:

  • D-8 Manutenção Programada
  • D-9 Validação de Vínculo
  • D-10 Votação formalizada
  • D-11 Anti-fraude avançada
  • D-13 Classificação de Risco
  • D-14 Agregação Multinível
  • D-15 Pesquisa e Exportação
  • D-16 Integridade Criptográfica
  • D-17 Controle de Mandato e Progressão

Colônias de lugares:

  • L-3 Validação e Qualidade (métodos restantes) — reincidência temporal, validação presencial sorteada e revalidação periódica requerem volume mínimo de dados
  • L-4 Análise de Cobertura e Gaps — depende de L-3
  • L-5 Incubação Social — depende de L-4

Colônias de empresa:

  • E-4 Impacto Territorial — depende de L-4 (gaps) que é Fase 2
  • E-5 Mapeamento de Estrutura Interna — clustering semântico de descrições de função, complexidade que não justifica no MVP

Nunca adiar (mesmo no MVP):

  • Observabilidade básica: logs com demanda_id e correlacao_id
  • Dado bruto preservado: normalização nunca descarta o original
  • Breakdown do score no ranking: auditabilidade desde o primeiro dia
  • Timeline pública de cada demanda: sem isso o ciclo não fecha
  • Metodologia de simulação de empresas: pública e replicável desde o primeiro diagnóstico