Pente fino do Participa e o caminho para o Decidim 0.33
Revisão do projeto inteiro para a transferência de tecnologia: aplicação host, decidim-govbr, módulos externos, multi-tenancy, CI e o chart Helm. Feita por leitura de código e das fontes upstream; nada foi executado em produção.
Resumo
O Decidim 0.33 ainda não foi lançado. O trabalho útil agora é preparar o terreno: destravar as gems, colocar os testes na CI e fechar as lacunas de operação.
decidim-govbr)decidim-govbrprepend do host sobre classes do Decidim, fora os dos módulos- Upgrade: não há 0.33 para instalar. O próximo passo real é o 0.32.2, ainda sem tag, com correções de segurança.
- Dependências: quase todas as gems seguem uma branch móvel, e três delas pertencem a terceiros (duas da Octree, uma em conta pessoal).
- Qualidade: os overrides têm testes, mas a CI só roda scans de segurança, todos com
allow_failure: true. - Operação: backup do Postgres está como TODO no chart, a aplicação conecta como superusuário e não há health check.
- Conhecimento: o README só cobre setup; as decisões de arquitetura não estão registradas neste repositório.
Tamanho da customização
Cerca de 51 mil linhas de código próprio sobre o Decidim; com traduções e testes, 81 mil.
| Onde | Código | Traduções | Testes |
|---|---|---|---|
App host (app/, config/, lib/) | 6,7 mil | 3,2 mil | 1,9 mil |
decidim-govbr | 4,4 mil | 0,5 mil | 1,3 mil |
14 gems externas, nas revisões do Gemfile.lock | 39,7 mil | 3,3 mil | 19,7 mil |
| Total | 50,8 mil | 7,0 mil | 22,9 mil |
Código é Ruby, views ERB, JS/SCSS e migrations próprias. Por tipo, no total: cerca de 29 mil linhas de Ruby, 8 mil de views e 12 mil de JS/SCSS.
| Gem externa | Código | Testes |
|---|---|---|
decidim-extra_blocks | 11,6 mil | 5,3 mil |
decidim-community_templates | 5,6 mil | 5,0 mil |
decidim-participatory_texts | 4,4 mil | 0,9 mil |
decidim-questionnaires | 3,4 mil | 1,9 mil |
decidim-government_spaces | 3,1 mil | 1,4 mil |
decidim-toggle (Octree) | 2,9 mil | 2,0 mil |
decidim-ephemeral_participation (Octree) | 2,3 mil | 0,2 mil |
decidim-apartment | 2,0 mil | 0,4 mil |
decidim-categories | 1,7 mil | 0,7 mil |
decidim-bp_meetings | 0,9 mil | 0,9 mil |
decidim-bp_comments | 0,8 mil | 0,7 mil |
decidim-extra_home_blocks | 0,7 mil | 0 |
decidim-bp_proposals | 0,2 mil | 0,2 mil |
omniauth-govbr | 0,1 mil | 0,02 mil |
- Das 39,7 mil linhas externas, 5,3 mil são de terceiros (as duas gems da Octree e o
omniauth-govbr); 34,4 mil são dos repositórios do LAPPIS. - São linhas físicas, com linhas em branco e comentários.
- Ficaram de fora as 770 migrations copiadas do Decidim, o
db/schema.rb,node_modulese o chart Helm. - O
config/do host inclui configuração padrão de Rails e do Decidim, então o número do host está um pouco acima do que é customização de fato.
Status do 0.33
Tudo que esta página diz sobre o 0.33 descreve a branch develop do Decidim (0.33.0.dev) em 2 de outubro de 2026 e ainda pode mudar. As release notes de develop são rascunho e listam só quatro itens reais; o diff de código é bem maior.
| Fato | Evidência |
|---|---|
| Última versão publicada é a 0.32.1, de 30 de julho de 2026 | GitHub releases e RubyGems |
Não existe tag, RC nem branch release/0.33-stable | API de tags do GitHub; a URL da branch devolve 404 |
CHANGELOG.md de develop está vazio | "Unreleased: Nothing." |
release/0.32-stable tem cerca de 65 commits depois da 0.32.1 | Um 0.32.2 parece próximo inferido |
| Política de suporte: só os dois últimos minors recebem correção de segurança | SECURITY.md; hoje são 0.32.x e 0.31.x |
| Data do 0.33.0 e fim do suporte ao 0.32 | Não publicados. Pela regra, o 0.32 sai de suporte quando o 0.34.0 for lançado inferido |
Para referência, o 0.32 teve RC em 24 de abril e versão final em 2 de julho de 2026. A regra upstream é subir um minor por vez, então 0.32 para 0.33 é um salto só.
O que muda no 0.33
Requisitos
| Item | 0.32 | develop (0.33) | Nosso estado |
|---|---|---|---|
| Ruby | 3.4.x | 3.4.x | 3.4.7, sem mudança |
| Rails | 8.1.x | 8.1.x | 8.1.3.1, sem mudança |
| Node | ≥ 22.14.0 | ≥ 24.18.0 | 22.14.0, precisa subir |
| npm | ≥ 9.6.7 | ≥ 11.16.0 | precisa subir |
| shakapacker | 9.7 | 10.3 | ^9.7.0, precisa subir |
| webpack | 5.105 (dependência) | 5.109 (peer dependency) | passa a ser nossa responsabilidade declarar |
Ações manuais confirmado
- Procedimento:
bundle update decidim,bin/rails decidim:upgrade,db:migrate,data:migrate. config/sidekiq.ymlé sobrescrito pelodecidim:upgrade. Filas próprias devem ir para um segundo arquivo (config/sidekiq.local.yml). Temos 15 filas no arquivo atual e outra lista no ConfigMap do Helm.- Nova fila
active_storage: precisa estar nosidekiq.ymle ser consumida por algum processo. - A função JS
initFoundationfoi removida; nenhum código pode depender defoundation-sites. - Perfis ocultos por padrão: participantes sem conta confirmada, sem aceite dos termos ou gerenciados deixam de aparecer no site e na API. Afeta qualquer view, cell ou consulta nossa que liste usuários.
- Verificações com bloqueio após 5 falhas e throttling por IP. O upstream pede revisão de handlers de autorização customizados; temos o
GovbrAuthorizationHandler. Isso também chega no 0.32.2.
Remoções e movimentações inferido do diff
- Dependências npm retiradas de
@decidim/core:foundation-sites,d3,emojibase,svg4everybody. - Datepicker movido de
src/decidim/datepicker/para um controller Stimulus emsrc/decidim/controllers/date_picker/. - Cells de wizard removidas (
wizard_step_form_celle variações). - Surveys: coluna
clean_after_publishe o job de limpeza removidos. - Atualizações maiores no frontend: React 18 para 19,
@rails/ujs6 para 7, ESLint 8 para 9. - Cerca de 28 controllers do admin alterados, provavelmente por escopo de organização nas buscas.
- "Steps" renomeado para "Phases" nas strings; overrides de i18n precisam de revisão.
Migrations que pesam inferido do diff
- Coluna
referencenova em posts, textos colaborativos, eleições, páginas e surveys, com preenchimento linha a linha. - Regeneração da referência de todo projeto de orçamento, sem volta.
- Data migrations que reescrevem títulos de debates, reuniões, eleições e propostas, sem volta e lentas em tabelas grandes.
- Com um schema por organização, tudo isso roda uma vez por tenant, em sequência.
Segurança relevante para multi-tenant inferido dos commits
- Impede uso de um JWT contra outra organização (PR #16673).
- Corrige acesso entre tenants na verificação por documento (PR #16666).
- Sanitização de páginas estáticas e blocos HTML (PR #16451).
Parte desses commits pode já estar no 0.32.x; o backport de cada um não foi conferido.
Onde quebra no nosso código
Colisões diretas com arquivos do host
| Arquivo nosso | O que aconteceu upstream | Risco |
|---|---|---|
app/packs/src/decidim/datepicker/generate_datepicker.js | Arquivo movido: nosso override deixa de ser carregado, sem erro | alto |
app/packs/src/decidim/datepicker/generate_timepicker.js | Idem; perdemos a correção de data local e os rótulos traduzidos | alto |
app/cells/decidim/comments/comment_form/show.erb | Template mudou; nossa cópia integral fica defasada | alto |
app/cells/decidim/comments/edit_comment_modal_form/show.erb | Template mudou | alto |
app/views/layouts/decidim/admin/_header.html.erb | Layout mudou; nossa cópia só acrescenta 5 linhas de PostHog | médio |
Essa lista só cobre arquivos no mesmo caminho do upstream. Os casos abaixo ficam em áreas alteradas e precisam de diff quando houver RC.
Overrides em áreas alteradas
- Comentários:
UpdateCommentOverride#calleCommentsControllerOverride#updatesubstituem o método inteiro, semsuper, e dependem de métodos privados do upstream. Odecidim-bp_commentsainda fazprependem 7 classes do mesmo módulo. - Propostas:
ProposalGCellOverridesobrescreveresource_image_path; a cell mudou upstream. - Painel system:
organizations_controllermudou;decidim-apartmentsubstituiindexeedit, edecidim-toggleaplica Deface no mesmo painel. - Layouts:
_head.html.erbeheader/_main_links_dropdown.html.erbmudaram;decidim-govbraplica Deface em_heade emheader/_main. - Deface em geral: os 10 overrides do
decidim-govbrdependem de seletores CSS dos templates (#accordion-duration,div#main-bar,.item_show__header). Se o seletor some, o override não aplica e não avisa. - Exportação de surveys:
ExportJobStreamingSurveyAnswerssobrescreveDecidim::ExportJob#performcom assinatura fixa e lê colunas dedecidim_forms_responses. - Menu do admin:
MenuReorderusainstance_variable_get(:@items)em objetos internos do Decidim.
Módulos e dependências
Todas as gems git usam branch: no Gemfile; nenhuma usa tag. O que garante a reprodução do build é só a revisão gravada no Gemfile.lock.
| Gem | Origem | Branch | Restrição Decidim | Bloqueia 0.33 | Invasividade |
|---|---|---|---|---|---|
decidim-govbr | no repositório | — | = 0.32.1 | sim | alta: 10 Deface, 12 views copiadas |
decidim-questionnaires | LAPPIS | main | ~> 0.32.1 | sim | alta: 11 Deface, ~15 prepend |
decidim-categories | LAPPIS | main | = 0.32.1 | sim | alta: 5 Deface, ~20 prepend |
decidim-extra_blocks | LAPPIS | main | < 0.33 | sim | alta: maior gem, 6 Deface |
decidim-ephemeral_participation | Octree | main | ~> 0.29 | não | alta: ~15 módulos, sem specs |
decidim-bp_comments | LAPPIS | main | ~> 0.32.1 | sim | média |
decidim-bp_meetings | LAPPIS | main | ~> 0.32.1 | sim | média |
decidim-government_spaces | LAPPIS | main | = 0.32.1 | sim | média; mexe nos mesmos forms que o govbr |
decidim-extra_home_blocks | LAPPIS | main | ~> 0.32.1 | sim | média, sem specs |
decidim-toggle | Octree | main | < 0.33 | sim | leve a média |
decidim-bp_proposals | LAPPIS | main | ~> 0.32.1 | sim | leve |
decidim-community_templates | LAPPIS | core/bump-32 | < 0.33.0 | sim | leve, mas o host aplica patches nela |
decidim-participatory_texts | LAPPIS | main | < 0.33.0 | sim | leve |
decidim-apartment | LAPPIS infra | v0.1.0-rc | ≥ 0.29.0 | não | estrutural; docs só citam 0.29 e 0.30 |
omniauth-govbr | conta pessoal no GitHub | main | nenhuma | não | estratégia OmniAuth simples |
Recomendações
- Trocar
branch:portag:em todas as gems e criar uma tag por versão do Decidim suportada. - Espelhar ou fazer fork das três gems de terceiros (
decidim-toggle,decidim-ephemeral_participation,omniauth-govbr) num grupo que a equipe controla. O login gov.br hoje depende de um repositório pessoal. - Manter uma matriz de compatibilidade por módulo e subir as gems na ordem das dependências:
toggleeephemeral_participationantes deextra_blocks. - Resolver a sobreposição entre
decidim-govbredecidim-government_spaces, que alteram o mesmo form e os mesmos comandos de processo participativo. - Colocar
decidim-govbrna ordem certa sem depender do nome de um initializer dodecidim-bp_comments.
Pente fino de arquitetura
Como o projeto altera o Decidim
| Mecanismo | Onde | Avaliação | Risco |
|---|---|---|---|
| Cópia integral de view, cell ou JS | ~15 arquivos no host, 12 no decidim-govbr | Diverge em silêncio a cada upgrade. Reduzir ao mínimo e anotar a versão de origem em cada cópia | alto |
prepend em to_prepare | config/initializers/decidim_*_overrides.rb | Padrão correto. Rever os que não chamam super ou usam métodos privados | médio |
| Redefinição de classe no caminho do upstream | app/cells/decidim/budgets/budget_information_modal_cell.rb | Substitui a classe inteira; converter em prepend | médio |
| Deface | módulos; precompilado no build, desligado em produção | Falha silenciosa quando o seletor muda. Falta teste que confirme que cada override aplicou | médio |
| Patch sobre patch | host aplica prepend nos módulos de override do bp_comments e reimplementa métodos do community_templates | Levar a mudança para a gem de origem | médio |
Multi-tenancy: um schema Postgres por organização
flowchart LR R[Requisicao HTTP] --> E[Elevator do decidim-apartment] E -->|host cadastrado| T[Schema UUID do tenant] E -->|host desconhecido ou /system| P[Schema public] T --> O[CurrentOrganization do Decidim] T --> J[Job enfileirado com o tenant nos argumentos] J --> S[Middleware do Sidekiq troca o schema] T --> A[ActiveStorage com prefixo do tenant]
| Ponto | O que o código faz | Risco |
|---|---|---|
| Migrations | Rodam em sequência em todos os tenants. O host tem apartment:schema_drift e apartment:repair_plan, sinal de que migrações parciais já ocorreram inferido | alto |
| Init do Apartment | O Dockerfile deixa APARTMENT_DISABLE_INIT=true na imagem final; nenhum chart desfaz. O setup de modelos excluídos pode não rodar em runtime inferido | alto |
| Jobs | O tenant vai como argumento extra do job e só o middleware do Sidekiq o remove. Jobs enfileirados por rake ou cron rodam em public ou sem tenant | médio |
| Cache | Namespace por tenant só quando REDIS_CACHE_URL existe. Sem ela, não há separação, e toda organização tem id 1 inferido | médio |
| Hosts | O elevator só resolve o host principal; secondary_hosts não são consultados. Host desconhecido cai em public | médio |
| Storage | Sem aws-sdk-s3 carregado, o serviço S3 vira disco local sem avisar | médio |
| Suporte da gem | A documentação do decidim-apartment só lista Decidim 0.29 e 0.30 | médio |
| Código morto | apartment:sync_schema_migrations tem corpo vazio; DetectOrganizationMiddleware nunca é incluído | baixo |
Essa é a decisão que mais encarece cada upgrade. Ela precisa de um ADR próprio e de um ensaio de migração numa cópia dos tenants antes de qualquer salto de versão.
Frontend
tailwind.config.jsna raiz é um arquivo gerado, com caminhos fixos paradecidim-core-0.32.1, e diverge do templateconfig/tailwind.config.js.erb(fonte e cores diferentes).decidim-govbr/package.jsonainda aponta para@decidim/*^0.30.1.config/webpack/custom.jsreferencia o service worker por um caminho interno do core.
Configuração e resíduos
config.load_defaults 6.1numa aplicação Rails 8.1.- Quase toda a configuração do Decidim vem de
credentials, com poucas alternativas por ENV.config/secrets.ymlcontinua no repositório, mas o Rails 8 não o lê. .disabled-for-0.32/tem README desatualizado: diz quecommunity_templatesnão tem versão para 0.32, mas a gem está ativa e com patches.- 17 migrations de
sortitionssem a gem no bundle;eager_load_pathsaponta paralib/decidim, que não existe. config/brakeman.ignoreepipeline_fixes.mdainda falam em Rails 7.0.10 e Decidim 0.30.1.- Quatro módulos do
decidim-govbrsão carregados e não parecem incluídos em lugar nenhum. - A rota
update_participatory_process_datesé declarada duas vezes emengine.rb.
Infra e Helm
Imagem e CI
- A imagem roda como root, mantém o toolchain de build e não define
CMDnemRAILS_ENV. Usanpm installem vez denpm ci. - Não existe
.dockerignore: um build local leva.env,.gitenode_modulespara dentro da imagem. - Os assets não são compilados no build; um job do Helm roda
shakapacker:compilea cada deploy, num volume compartilhado. - A CI tem só gitleaks, hadolint, brakeman e SCA, todos com
allow_failure: true. Nenhum job roda RSpec, RuboCop, ESLint ou Stylelint. build_bump_imagesó dispara na branchrelease/0.32.1-v1.0.0. Não há job de deploy.
Chart decidim-helm 0.3.12
Repositório lappis-unb/decidimbr/infra/charts/decidim-helm, main em c996356. A cópia local está igual ao remoto.
| Achado | Detalhe | Risco | Branch que já trata |
|---|---|---|---|
| Sem backup do Postgres | Backup e ScheduledBackup comentados como TODO; cluster CNPG com 1 instância | alto | feat/postgres-s3-backup |
| Aplicação como superusuário | Credenciais vêm do secret postgresSuperuser | alto | feat/db-managed-roles-bootstrap |
| Sem CronJobs | Métricas, lembretes, open data e limpeza de dados não são agendados | alto | nenhuma |
| Sem volume para uploads | Só existe o PVC de assets. Com STORAGE_PROVIDER ausente, uploads vão para disco efêmero inferido | alto | nenhuma |
| Filas divergentes | O ConfigMap omite metrics e spam_analysis, que existem no sidekiq.yml do app | médio | nenhuma |
| Defaults de outro app | Imagem upstream 0.31.4, RAILS_ENV=development, DECIDIM_FORCE_SSL=false | médio | fix/chart-defaults-0.32.1 |
| Probes sem valor | Web só com TCP; Sidekiq com test -d /proc/1, que sempre passa | médio | nenhuma |
| Redis sem autenticação | auth.enabled: false nos dois subcharts | médio | nenhuma |
| Senha de admin em ConfigMap | O job de secrets lê a senha do admin de sistema de um ConfigMap em texto puro | médio | nenhuma |
| Job de upgrade sem hook do Argo CD | db-upgrade-job só tem anotações de hook do Helm | médio | nenhuma |
| Ingress | Gateway API com HTTPRoute; Ingress padrão opcional | baixo | feat/standard-ingress-support |
Há também feat/external-postgres-secret. Cinco branches abertas tratam problemas desta lista e ainda não entraram em main. O chart antigo em deploy/decidim-instance-chart (appVersion 0.29.3) segue no repositório do app e deve ser removido ou marcado como legado.
Para o 0.33, o chart precisa de três mudanças: imagem com Node 24, a fila active_storage no ConfigMap do Sidekiq e o passo data:migrate conferido no job de upgrade, que já o executa.
Observabilidade
- Não há endpoint de health check na aplicação.
- Logs só vão para STDOUT com
RAILS_LOG_TO_STDOUT, que os charts não definem. - Não há rastreamento de erros, métricas da aplicação nem logs estruturados. Só Redis e Postgres expõem métricas.
- O Pyroscope marca tudo como
process=web, inclusive dentro do Sidekiq.
Segurança e dados pessoais
| Achado | Onde | Risco |
|---|---|---|
Credenciais antigas continuam no histórico do git; a CI passou a usar --no-git e não varre mais o histórico. A rotação é dada como obrigatória e não foi possível confirmar se ocorreu | pipeline_fixes.md, .gitleaksignore (20 exceções) | alto |
CPF e document_number fora do filtro de parâmetros de log | config/initializers/filter_parameter_logging.rb | alto |
/sidekiq aceita qualquer usuário admin; a fila é compartilhada entre tenants inferido | config/routes.rb | alto |
CSP com unsafe-inline e unsafe-eval em script-src | config/initializers/content_security_policy01.rb | médio |
| SMTP sem verificação de certificado TLS | config/environments/production.rb | médio |
PostHog com host padrão no SaaS dos EUA; variáveis fora do .env.sample | config/initializers/posthog.rb | médio |
| Senha de exemplo fixa nos comandos de criação de admin | README.md | médio |
| Endpoint interno de storage citado em comentário | .env.sample | baixo |
Ferramentas de scan em :latest e instaladas por curl | sh | .gitlab-ci.yml | baixo |
A varredura por padrões de chave e token nos arquivos rastreados dos dois repositórios não encontrou credenciais reais no estado atual.
Transferência de tecnologia
Assumir a responsabilidade pede mais que acesso ao código. Estes são os itens que hoje dependem de memória de pessoas ou de repositórios fora do nosso controle.
| Tema | Situação | O que precisa existir |
|---|---|---|
| Decisões de arquitetura | Comentários citam openspec/ e docs/, ausentes neste checkout | ADRs no repositório: multi-tenancy por schema, estratégia de override, Deface precompilado, storage |
| Testes | 41 arquivos de spec fora da CI; o app de teste do decidim-govbr precisa ser gerado | Job de RSpec obrigatório para host e govbr; scans sem allow_failure depois de zerar os achados |
| Posse das gems | 14 repositórios, 3 de terceiros, 12 em branch móvel | Lista de donos, permissões no GitLab, tags, forks dos terceiros |
| Configuração de produção | Os values reais não estão em nenhum dos dois repositórios | Values versionados (sem segredos) e inventário de secrets do cluster |
| Upgrade | Histórico só nos commits; quatro saltos desde 0.29 | Runbook: ordem das gems, ensaio com cópia dos tenants, auditoria de drift, volta atrás |
| Operação | Sem cron, sem backup, sem health check | Runbook de incidentes, restauração testada, criação e remoção de tenant |
| Credenciais | Rotação pós-vazamento não confirmada; master.key fora do repositório | Registro de rotação e guarda formal das chaves e do cliente gov.br |
| Documentação de entrada | README só com setup local | Mapa dos módulos, do fluxo de tenant e de como alterar o Decidim neste projeto |
Roteiro sugerido
Fechar o risco operacional
- RSpec na CI para host e
decidim-govbr. - Backup do Postgres e usuário de aplicação sem superusuário (branches já abertas no chart).
- CronJobs das tarefas recorrentes, tenant a tenant.
- Filas do Sidekiq iguais entre app e chart.
- Confirmar rotação de credenciais; filtrar CPF nos logs; restringir
/sidekiq. - Aplicar o 0.32.2 quando sair a tag.
Destravar e reduzir a superfície
- Tags e forks das gems; relaxar as restrições na ordem das dependências.
- Node 24 e shakapacker 10 numa branch de ensaio.
- Trocar cópias integrais por
prependou levar a mudança para a gem. - Portar os dois overrides de datepicker para o controller Stimulus.
- Teste que verifica cada Deface aplicado.
- ADRs e runbook de upgrade; ensaio de migração em cópia dos tenants.
Executar o upgrade
- Reler as release notes: o rascunho atual está incompleto.
- Comparar as gems 0.32.1 instaladas com a tag do RC, arquivo por arquivo, para cada override.
- Mover filas próprias para
sidekiq.local.ymle incluiractive_storage. - Rever o
GovbrAuthorizationHandlere as listagens de usuários. - Migrar tenant a tenant, medindo o tempo das data migrations.
A verificar
Pontos deduzidos da leitura que ainda pedem confirmação num ambiente em execução ou com quem opera a plataforma.
- O efeito de
APARTMENT_DISABLE_INIT=truenum pod de produção. - Se jobs das filas
metricsespam_analysisestão se acumulando no Redis. - Os values reais de produção:
STORAGE_PROVIDER,REDIS_CACHE_URL,RAILS_ENV,RAILS_LOG_TO_STDOUT. - Se as tarefas recorrentes rodam por algum mecanismo fora dos charts.
- Se a rotação das credenciais que estiveram no histórico foi feita.
- Se os quatro módulos soltos do
decidim-govbrsão mesmo código morto. - Quantos tenants existem e o tamanho das tabelas de propostas, para estimar a janela de migração.
- O backport de cada correção de segurança citada para o 0.32.x.