Participa — como é concebido e o que entrega (roadmap + top 10)
Explicação de mudanças · Decidim · GovBR

Participa: como o sistema é concebido e o que ele entrega

Uma leitura do repositório inteiro — 262 commits, de 20/05/2025 a 10/09/2026 — organizada como um roadmap (Initiatives → Projects → Cycles → Issues → Sub-issues), com destaque para as 10 funcionalidades que carregam o valor do produto.

Stack: Ruby 3.4.7 · Rails 8.1.3.1 · Decidim 0.32.1 Commits: 262 Janela: 20/05/2025 → 10/09/2026 Gems externas (git): 11

1Contexto

Antes de olhar o que mudou, vale entender o que é este sistema. Há dois níveis: uma introdução para quem nunca viu Decidim (pode pular se você já conhece) e, em seguida, o recorte específico deste repositório.

1.1 Decidim para iniciantes

O Decidim é um framework de democracia participativa escrito em Ruby on Rails. A ideia central é que uma prefeitura ou um ministério queira abrir parte da sua gestão para participação popular — orçamento, consultas, propostas, audiências — e o Decidim dá a estrutura pronta para isso.

Três conceitos organizam tudo:

  • Organization (organização) — o “inquilino”. Cada órgão (Ministério, Prefeitura) é uma organização, com domínio e identidade próprios.
  • Participatory space (espaço participativo) — o contêiner do processo. Exemplos: Processo Participativo (um ciclo de consulta com etapas), Assembleia, Conferência, Iniciativa (proposta de lei por assinaturas).
  • Component (componente) — a ferramenta dentro do espaço. Exemplos: Proposals, Meetings, Budgets, Surveys, Blogs, Pages, Accountability, Debates, Sortitions.

Um app Decidim “gerado” é surpreendentemente fino: um punhado de arquivos de configuração mais o Gemfile. Todo o comportamento vem das dependências (engines). A customização de um projeto Decidim costuma virar uma engine própria — uma gem de caminho local que estende as views e classes do core sem fazer monkey-patch, geralmente com Deface (que reescreve trechos de HTML do core por seletor).

Glossário mínimo

Engine: mini-app Rails embutido no principal. Cell: uma “view com lógica” (ViewModel) — no Decidim, o padrão preferido no lugar de partials. Deface: reescreve views do core por seletor CSS, sem copiar o arquivo inteiro.

1.2 O terreno deste repositório

O repositório participa segue exatamente esse desenho — é um wrapper Rails fino sobre o Decidim, onde a customização vive quase toda em uma engine e em módulos externos.

CamadaOndeO que faz
App Railsapp/ (≈44 arquivos), config/Casca fina: overrides pontuais do core, rotas, 21 initializers, entrypoint do Decidim.
Engine própriadecidim-govbr/Toda a UI/UX GovBR: cells, overrides Deface, content blocks, services, SCSS/JS. Instalada como gem path:.
Módulos externosGemfile (11 gems via git)Funcionalidades de participação (questionários, propostas, reuniões, comentários, categorias, espaços de governo, iniciativas, conferências, textos participativos) mantidas em repositórios GitLab/GitHub separados.
Multi-tenantros-apartment + decidim-apartmentUm schema PostgreSQL por organização. O coração de “um Decidim, muitos órgãos”.
Deploydeploy/, decidim-helm/, DockerfileDocker multi-stage e charts Helm (instância + provisionamento de organização).

Repare no que não existe: não há um diretório gordo de regras de negócio. O valor do produto mora na composição — identidade gov.br + pele GovBR + pacote de módulos + infraestrutura multi-tenant. É o inverso de “escrever tudo do zero”: a equipe estende um framework maduro e mantém, à parte, os módulos que precisaram divergir.

2Intuição

Duas ideias explicam quase todo o repositório. A primeira é arquitetural; a segunda é a forma como o produto foi construído.

2.1 Um Decidim, um banco, muitos órgãos

Suponha que o Ministério X e a Prefeitura Y queiram usar a mesma instância do Participa. Se cada um rodasse seu próprio servidor, seriam dois deploys, duas atualizações, dois bancos — caro e fragmentado. A alternativa é compartilhar o código e o servidor, isolando apenas os dados.

É isso que “one Decidim, one DB” significa: um banco PostgreSQL, mas um schema por organização. Schema, no Postgres, é um namespace de tabelas dentro do mesmo banco. Então as tabelas têm o mesmo formato nos dois schemas:

Puma (Rails + Decidim) host: ministx.participa… Apartment switch! → schema PostgreSQL — participa_production public · decidim_organizations (global) shared_extensions schema: ministx decidim_users, decidim_proposals, … schema: prefy decidim_users, decidim_proposals, … mesmas tabelas; dados isolados
Mesmo schema lógico em cada organização; o public guarda o que é global (lista de organizações).

O ros-apartment faz o trabalho pesado: ao entrar uma requisição, ele descobre a organização pelo host e muda o search_path da conexão para o schema certo. Do ponto de vista do Decidim, parece que só existe uma organização. Existe uma pegadinha: as migrations precisam rodar em todos os schemas — daí as rake tasks apartment_sync / apartment_audit / apartment_repair_plan.

2.2 Três camadas sobrepostas

A segunda intuição vem do histórico: o Participa não foi “projetado de uma vez”. Ele ganhou identidade em três camadas que se empilharam ao longo dos meses — e é isso que o roadmap da seção 4 mostra.

  1. Fundação multi-tenant (ago/2025) — “um Decidim, um banco, múltiplos schemas”.
  2. Pele GovBR + identidade (ago–out/2025 e jun/2026) — login gov.br, login efêmero e o Design System do Governo Federal por cima da UI do Decidim.
  3. Expansão de módulos (2025–2026) — o pacote que transforma a plataforma de “democracia digital básica” num ecossistema de participação.

3Top 10 funcionalidades

Se o repositório tivesse que ser resumido em dez entregas de valor, seriam estas — cada uma com a evidência no código e no histórico.

#FuncionalidadeValor entregueEvidência
1Multi-tenancy “one Decidim, one DB”Vários órgãos numa única instância, com dados isolados por schema.6f5214b · ros-apartment, decidim-apartment, lib/tasks/apartment_*.rake
2Login gov.br (OAuth2 + PKCE)Entrada com a identidade oficial do cidadão; credenciais por organização.46cf093 · config/initializers/omniauth_govbr.rb
3Login efêmeroParticipar sem conta gov.br — reduz a barreira de entrada.b0ad8c3 · config/initializers/ephemeral_auth.rb
4Autorização por CPFElegibilidade verificada (CPF, nome, e-mail) antes de agir.decidim-govbr/app/services/govbr_authorization_handler.rb
5Pele GovBR / Design SystemConformidade visual com o padrão do Governo Federal.7a77e07, 0c6eb42 (navbar), a494b6b
6Content blocks da homeHomepage institucional configurável (hero, notícias, processos abertos, estatísticas).2bd3b76, ad39e77, faf1253
7Provisionamento de tenants via HelmSubir uma instância e criar organizações com um comando.95dbbf3, 752a62d (“automate org creation”)
8SaaS demo self-serviceCriar organização pela UI, sem editar YAML (SMTP, i18n, validações).82d9b96, 6002c35, 03b5c00
9Pacote de módulos de participaçãoQuestionários, propostas, reuniões, comentários, categorias, espaços de governo, iniciativas, conferências, textos participativos.97f6c6c, 803b6c4, cf7fd36, e63170c
10Operação & confiançaCI/CD com 4 scanners de segurança, imagem Docker multi-stage, release por tag e observabilidade (PostHog/Matomo/Pyroscope).6a177ed, 2936b60, c7b0b0d
Como ler

As funcionalidades 1–4 são infraestrutura e identidade (o que torna a plataforma plausível para o governo); 5–9 são produto e experiência (o que o cidadão e o gestor veem); 10 é o alicerce de entrega que mantém tudo no ar.

4Roadmap

O mesmo histórico, agora organizado na taxonomia pedida. Cada nível responde a uma pergunta diferente:

🚀 Initiative — meta estratégica 📂 Project — esforço + dono 🔄 Cycle — janela no tempo 🎫 Issue — tarefa acionável 📎 Sub-issue — checkbox de código
Da meta estratégica ao checkbox de código — cada nível estreita o escopo e o horizonte de tempo.

4.1 Os Cycles (janelas de execução)

O projeto não teve “sprints” formais, mas a cadência dos commits desenha janelas claras. Derivamos sete cycles a partir das datas reais:

CycleJanelaTemaMarcos
C0mai/2025Fundaçãocontainerização, Sidekiq, primeiro chart Helm, rake de admin
C1ago–set/2025Multi-tenant & identidade“one decidim one db”, instalação do Deface, login omniauth GovBR, CI Docker
C2set–out/2025UX + provisionamentologin efêmero, design do efêmero, homepage com subcomponentes, seed de orgs por JSON, chart de tenants, pipeline de segurança
C3out/2025–mar/2026Templates, testes, SaaS demogem de templates, SaaS demo (SMTP/i18n), CLI de testes, primeiras suítes, split Redis cache/queue
C4abr–mai/2026Formulários e componentesformulários simplificados, textos participativos, bump 0.30.9, S3/MinIO, ordenação de componentes
C5jun/2026Reforma visual govbr-dsTailwind override, design system adaptativo (admin), navbar Brasil Participativo, movimentação datas→etapas
C6jul–set/2026Módulos & polimentoiniciativas, conferências, módulos bp_*, content blocks em cards nativos, i18n pt-BR, upgrade 0.32.1

4.2 Initiatives → Issues

Abaixo, as cinco Initiatives estratégicas. Para cada uma: os Projects com dono e escopo, o Cycle em que aconteceram, e a decomposição em Issues → Sub-issues concretas.

🚀 I1 — Identidade gov.br confiança & elegibilidade

Meta: o cidadão entra e age com a identidade oficial do governo, sem barreira desnecessária.

Projects: Login gov.br (dono: Paulo Tada) · Login efêmero (Daniela Oliveira, Maria Eduarda) · Autorização CPF

CycleIssueSub-issues (código)
C1Integrar gov.br via OAuth2gem omniauth-govbr; omniauth_govbr.rb; corrigir scopes/redirect/secrets
C2Habilitar auth gov.br por organizaçãoprovider lê credentials por org; config/initializers/omniauth_govbr.rb
C2Login efêmeroephemeral_auth.rb registra workflow ephemeral = true; b0ad8c3
C2Autorização por CPFGovbrAuthorizationHandler: valida 11 dígitos, nome ≥ 2, e-mail; unique_id = SHA256

🚀 I2 — Um Decidim, muitos órgãos multi-tenant

Meta: servir múltiplos órgãos numa instância só, isolando dados e automatizando o provisionamento.

Projects: one-decidim-one-db (Hadrien Froger, Leonardo) · Provisionamento Helm (Leonardo) · Sync de schemas (Paulo Tada)

CycleIssueSub-issues (código)
C1Isolar por schemaros-apartment (require apartment) + gem decidim-apartment; dump_schemas="public" em application.rb
C2Chart para criar tenants95dbbf3; depois 752a62d “automate org creation”
C3Separar Redis cache de queuec51cacd; portas 6379/6380
C5Sincronizar schemasb30444d + lib/tasks/apartment_sync.rake

🚀 I3 — Pele GovBR identidade visual

Meta: a plataforma parecer, sem ambiguidade, um serviço do Governo Federal.

Projects: govbr-ds POC (VictorJorgeFGA) · Navbar (Gustavo Henrique) · Admin adaptativo

CycleIssueSub-issues (código)
C5Override base com Tailwind7a77e07; tailwind.config.js; SCSS em decidim-govbr/app/packs
C5Design system adaptativo no admina494b6b; _design_system_admin.scss
C5Navbar Brasil Participativo0c6eb42; views layouts/decidim/header/*
C5/C6Ajuste fino de cores/footer/breadcrumb6 commits de VictorJorgeFGA em jun/2026

🚀 I4 — Cobertura de participação módulos

Meta: cobrir os formatos de participação social que cada órgão precisa.

Projects: Pacote de módulos (Paulo Tada) · Content blocks da home (Paulo Tada, Medeiros) · Templates comunitários

CycleIssueSub-issues (código)
C2Homepage em subcomponentes2bd3b76; cells em decidim-govbr/app/cells/.../homepage/content_block/
C3Templates comunitáriosgem decidim-community_templates (9fb95b6)
C4Textos participativosgem decidim-participatory_texts (9c3306c)
C5Módulos bp_* + categorias + espaços de governo97f6c6c, 803b6c4, 7573151
C6Iniciativas e conferênciascf7fd36, e63170c; gems decidim-initiatives, decidim-conferences
C5Cards nativos e blocos configuráveisfaf1253, e022a43, f77cebc (botão “ver mais”)

🚀 I5 — Operação, deploy e confiança alicerce

Meta: entregar com segurança, automatizar ambientes e enxergar o que acontece em produção.

Projects: Docker/CI (Leonardo, zlimaz) · Helm (Leonardo) · Observabilidade (Arthur Melo)

CycleIssueSub-issues (código)
C0–C1Container & build multi-stageDockerfile; 68be5fd
C1CI de build e release por tag2936b60; .gitlab-ci.yml
C2Pipeline de segurança (4 scanners)6a177ed: Gitleaks, Hadolint, Brakeman, Grype/Syft
C3SaaS demo self-service82d9b96, 6002c35, 03b5c00
C3ObservabilidadeOpenTelemetry (30a9033) → substituído por Pyroscope (c7b0b0d); PostHog, Matomo

5Passeio pelo código

Agora, onde cada peça do roadmap vive no repositório. Agrupado para leitura linear, não por pasta.

5.1 A casca do app e a engine

O app/ da raiz guarda apenas overrides do core (Comments, Proposals, Reports, Matomo) e o entrypoint DecidimController. O grosso está na engine:

# decidim-govbr/lib/decidim/govbr/engine.rb (resumo)
initializer "decidim-govbr.webpacker.assets_path" do
  Decidim.register_assets_path File.expand_path("app/packs", root)
end

initializer "decidim-govbr.homepage_content_blocks" do
  Decidim::Govbr::Homepage::ContentBlocks.register_homepage_content_blocks!
end

initializer "decidim-govbr.optional_hero_text_and_cta",
            after: "decidim_core.homepage_content_blocks" do
  manifest = Decidim.content_blocks.for(:homepage).find { |b| b.name == :hero }
  manifest.cell = "decidim/govbr/homepage/content_block/hero"   # repointa o bloco do core
end

Note o padrão: a engine não recria o bloco hero — ela reaponta o manifest já registrado pelo core para a sua própria cell. Como ContentBlockRegistry#register não aceita nome duplicado, a única saída é mutar o manifest existente. É um detalhe de framework que explica vários comentários longos no código.

5.2 Multi-tenant

O config/initializers/apartment.rb é deliberadamente trivial — a mágica vive na gem decidim-apartment:

Rails.application.config.after_initialize do
  ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a
end

# config/application.rb — a razão de dump_schemas ser "public":
# sem isto, o dumper do Rails 8.1 prefixa toda tabela com o schema e o
# schema.rb deixa de carregar (create_table "public.decidim_users" …).
config.active_record.dump_schemas = "public"

5.3 Identidade: gov.br e efêmero

O login efêmero é quase todo declarativo — repare em ephemeral = true:

# config/initializers/ephemeral_auth.rb
Decidim::Verifications.register_workflow(:govbr_authorization_handler) do |workflow|
  workflow.ephemeral = true
  workflow.form = "GovbrAuthorizationHandler"
end

E a regra de elegibilidade, em Ruby direto:

# decidim-govbr/app/services/govbr_authorization_handler.rb
def unique_id
  Digest::SHA256.hexdigest("#{document_number}-#{Rails.application.secret_key_base}")
end

def validate_document_number
  cpf = document_number.gsub(/\D/, '')
  errors.add(:document_number, I18n.t('errors.messages.invalid_document')) unless cpf.length == 11
end

5.4 Content blocks: a home como produto

prefeitura-y.participa.gov.br Brasil Participativo · navbar (govbr-ds) hero (show_welcome_text / show_cta_button) Participe → blog_news — 1 destaque + 3 secundárias ver mais → open_processes — abas por taxonomia Entrar sessões personalizadas (devise) Entrar com gov.br Entrar sem cadastro (efêmero) /users/sign_in
A home (esquerda) é composta por content blocks; o login (direita) oferece gov.br e efêmero.

A cell de notícias mostra o contrato real — busca os posts publicados da organização atual:

# decidim-govbr/app/cells/decidim/govbr/homepage/content_block/blog_news_cell.rb
def recent_posts
  @recent_posts ||= Decidim::Blogs::Post
                    .published
                    .where(component: blogs_components)
                    .created_at_desc
                    .limit(4)
                    .to_a
end

5.5 Deploy e CI

O .gitlab-ci.yml tem seis estágios: security → build_dev_image → build_bump_image → build_prod_image → reports → release. Os quatro scanners rodam com allow_failure: true e geram um security-reports.zip. A imagem prod é construída só em tags, disparando um release com changelog gerado a partir dos commits.

5.6 Observabilidade

Três frentes em initializers separados: matomo.rb (somente o container do Tag Manager), posthog.rb e pyroscope.rb (profiling contínuo, só em produção). O Pyroscope substituiu o OpenTelemetry — uma decisão de custo/simplicidade registrada em c7b0b0d.

6Código ↔ valor: gems externas

Boa parte do valor não está neste repositório. Está em módulos mantidos à parte e consumidos por git. Para rastrear uma funcionalidade até o código, é preciso ir ao repositório externo, na revisão fixada no Gemfile.lock.

Gem (valor)RepositórioBranch @ revisão
omniauth-govbr (SSO gov.br)github.com/paulohtfs/omniauth-govbrmain @ 5e9c9282
decidim-community_templatesgitlab.com/lappis-unb/decidimbr/…/decidim-bp_templatescore/bump-32 @ 4784a016
decidim-participatory_texts…/decidim-participatory_textmain @ d4111173
decidim-questionnaires…/decidim-module-questionnairesmain @ c778fdc8
decidim-bp_proposals…/decidim-bp_proposalsmain @ 0447023f
decidim-bp_meetings…/decidim-bp_meetingsmain @ 6fe03305
decidim-bp_comments…/decidim-bp_commentsmain @ c5be9a81
decidim-government_spaces…/decidim-government-spacesmain @ cba43425
decidim-categories…/decidim-categoriesmain @ 66919442
decidim-extra_home_blocks…/decidim-extra_home_blocksmain @ b4a41ac4
decidim-apartment (multi-tenant)…/infra/participa-gemv0.1.0-rc @ 1eb5c0da
decidim-govbr (esta engine)local ./decidim-govbr— (path)
Atenção ao cruzar

Um bump: neste repositório pode mudar comportamento sem nenhum diff local — foi assim em e3cd646 core: update modules. Ao investigar uma feature, confirme a revisão em Gemfile.lock e leia o módulo externo naquele commit.

7Casos-limite e armadilhas

Upgrade para 0.32.1

O salto 0.30.9 → 0.32.1 (set/2026) deixou o diretório .disabled-for-0.32/ com arquivos deliberadamente retidos: eles dependem de decidim-community_templates, que não tem release compatível com 0.32. Não são lixo — há um README.md decidindo mantê-los.

Locales vêm de ENV

DECIDIM_AVAILABLE_LOCALES e DECIDIM_DEFAULT_LOCALE (default en) substituem o antigo secrets.yml — o Rails 8.1 removeu Rails.application.secrets. Remover um locale que a organização usa quebra os formulários admin com I18n::InvalidLocale.

Repositório git aninhado

decidim-helm/ é um repositório git próprio dentro deste. Editar arquivos ali não aparece no git diff da raiz. O chart em uso vive em deploy/.

Achado: .gitignore com marcadores de conflito

O .gitignore versionado contém marcadores de merge (<<<<<<< Updated upstream>>>>>>> Stashed changes) no fim do arquivo, e ainda lista .gitnexus dentro do bloco. Vale limpar — hoje os padrões soltos são ignorados silenciosamente.

8Como ler isto

A página foi escrita para ser lida de cima para baixo, mas ela também funciona como referência: a seção 3 dá o resumo executivo, a 4 dá a navegação por investimento, a 5–6 dão o cruzamento código ↔ valor.

Há um quiz de cinco perguntas esperando você no chat — ele quer checar se a substância (e não o texto) ficou. Quando terminar de ler, diga que está pronto.

Origem dos dados

Extraído de git log (262 commits), Gemfile/Gemfile.lock, decidim-govbr/, config/initializers/, deploy/, Dockerfile, .gitlab-ci.yml, HISTORIA.md e AGENTS.md. Nada foi inferido sem evidência no repositório.

Participa — explicação de mudanças · documento gerado a partir do repositório em seu estado atual. Fonte editável: docs/participa-roadmap.html.