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).
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.
| Camada | Onde | O que faz |
|---|---|---|
| App Rails | app/ (≈44 arquivos), config/ | Casca fina: overrides pontuais do core, rotas, 21 initializers, entrypoint do Decidim. |
| Engine própria | decidim-govbr/ | Toda a UI/UX GovBR: cells, overrides Deface, content blocks, services, SCSS/JS. Instalada como gem path:. |
| Módulos externos | Gemfile (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-tenant | ros-apartment + decidim-apartment | Um schema PostgreSQL por organização. O coração de “um Decidim, muitos órgãos”. |
| Deploy | deploy/, decidim-helm/, Dockerfile | Docker 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:
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.
- Fundação multi-tenant (ago/2025) — “um Decidim, um banco, múltiplos schemas”.
- 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.
- 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.
| # | Funcionalidade | Valor entregue | Evidência |
|---|---|---|---|
| 1 | Multi-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 |
| 2 | Login gov.br (OAuth2 + PKCE) | Entrada com a identidade oficial do cidadão; credenciais por organização. | 46cf093 · config/initializers/omniauth_govbr.rb |
| 3 | Login efêmero | Participar sem conta gov.br — reduz a barreira de entrada. | b0ad8c3 · config/initializers/ephemeral_auth.rb |
| 4 | Autorização por CPF | Elegibilidade verificada (CPF, nome, e-mail) antes de agir. | decidim-govbr/app/services/govbr_authorization_handler.rb |
| 5 | Pele GovBR / Design System | Conformidade visual com o padrão do Governo Federal. | 7a77e07, 0c6eb42 (navbar), a494b6b |
| 6 | Content blocks da home | Homepage institucional configurável (hero, notícias, processos abertos, estatísticas). | 2bd3b76, ad39e77, faf1253 |
| 7 | Provisionamento de tenants via Helm | Subir uma instância e criar organizações com um comando. | 95dbbf3, 752a62d (“automate org creation”) |
| 8 | SaaS demo self-service | Criar organização pela UI, sem editar YAML (SMTP, i18n, validações). | 82d9b96, 6002c35, 03b5c00 |
| 9 | Pacote de módulos de participação | Questionários, propostas, reuniões, comentários, categorias, espaços de governo, iniciativas, conferências, textos participativos. | 97f6c6c, 803b6c4, cf7fd36, e63170c |
| 10 | Operação & confiança | CI/CD com 4 scanners de segurança, imagem Docker multi-stage, release por tag e observabilidade (PostHog/Matomo/Pyroscope). | 6a177ed, 2936b60, c7b0b0d |
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:
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:
| Cycle | Janela | Tema | Marcos |
|---|---|---|---|
| C0 | mai/2025 | Fundação | containerização, Sidekiq, primeiro chart Helm, rake de admin |
| C1 | ago–set/2025 | Multi-tenant & identidade | “one decidim one db”, instalação do Deface, login omniauth GovBR, CI Docker |
| C2 | set–out/2025 | UX + provisionamento | login efêmero, design do efêmero, homepage com subcomponentes, seed de orgs por JSON, chart de tenants, pipeline de segurança |
| C3 | out/2025–mar/2026 | Templates, testes, SaaS demo | gem de templates, SaaS demo (SMTP/i18n), CLI de testes, primeiras suítes, split Redis cache/queue |
| C4 | abr–mai/2026 | Formulários e componentes | formulários simplificados, textos participativos, bump 0.30.9, S3/MinIO, ordenação de componentes |
| C5 | jun/2026 | Reforma visual govbr-ds | Tailwind override, design system adaptativo (admin), navbar Brasil Participativo, movimentação datas→etapas |
| C6 | jul–set/2026 | Módulos & polimento | iniciativas, 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
| Cycle | Issue | Sub-issues (código) |
|---|---|---|
| C1 | Integrar gov.br via OAuth2 | gem omniauth-govbr; omniauth_govbr.rb; corrigir scopes/redirect/secrets |
| C2 | Habilitar auth gov.br por organização | provider lê credentials por org; config/initializers/omniauth_govbr.rb |
| C2 | Login efêmero | ephemeral_auth.rb registra workflow ephemeral = true; b0ad8c3 |
| C2 | Autorização por CPF | GovbrAuthorizationHandler: 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)
| Cycle | Issue | Sub-issues (código) |
|---|---|---|
| C1 | Isolar por schema | ros-apartment (require apartment) + gem decidim-apartment; dump_schemas="public" em application.rb |
| C2 | Chart para criar tenants | 95dbbf3; depois 752a62d “automate org creation” |
| C3 | Separar Redis cache de queue | c51cacd; portas 6379/6380 |
| C5 | Sincronizar schemas | b30444d + 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
| Cycle | Issue | Sub-issues (código) |
|---|---|---|
| C5 | Override base com Tailwind | 7a77e07; tailwind.config.js; SCSS em decidim-govbr/app/packs |
| C5 | Design system adaptativo no admin | a494b6b; _design_system_admin.scss |
| C5 | Navbar Brasil Participativo | 0c6eb42; views layouts/decidim/header/* |
| C5/C6 | Ajuste fino de cores/footer/breadcrumb | 6 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
| Cycle | Issue | Sub-issues (código) |
|---|---|---|
| C2 | Homepage em subcomponentes | 2bd3b76; cells em decidim-govbr/app/cells/.../homepage/content_block/ |
| C3 | Templates comunitários | gem decidim-community_templates (9fb95b6) |
| C4 | Textos participativos | gem decidim-participatory_texts (9c3306c) |
| C5 | Módulos bp_* + categorias + espaços de governo | 97f6c6c, 803b6c4, 7573151 |
| C6 | Iniciativas e conferências | cf7fd36, e63170c; gems decidim-initiatives, decidim-conferences |
| C5 | Cards nativos e blocos configuráveis | faf1253, 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)
| Cycle | Issue | Sub-issues (código) |
|---|---|---|
| C0–C1 | Container & build multi-stage | Dockerfile; 68be5fd |
| C1 | CI de build e release por tag | 2936b60; .gitlab-ci.yml |
| C2 | Pipeline de segurança (4 scanners) | 6a177ed: Gitleaks, Hadolint, Brakeman, Grype/Syft |
| C3 | SaaS demo self-service | 82d9b96, 6002c35, 03b5c00 |
| C3 | Observabilidade | OpenTelemetry (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
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ório | Branch @ revisão |
|---|---|---|
omniauth-govbr (SSO gov.br) | github.com/paulohtfs/omniauth-govbr | main @ 5e9c9282 |
decidim-community_templates | gitlab.com/lappis-unb/decidimbr/…/decidim-bp_templates | core/bump-32 @ 4784a016 |
decidim-participatory_texts | …/decidim-participatory_text | main @ d4111173 |
decidim-questionnaires | …/decidim-module-questionnaires | main @ c778fdc8 |
decidim-bp_proposals | …/decidim-bp_proposals | main @ 0447023f |
decidim-bp_meetings | …/decidim-bp_meetings | main @ 6fe03305 |
decidim-bp_comments | …/decidim-bp_comments | main @ c5be9a81 |
decidim-government_spaces | …/decidim-government-spaces | main @ cba43425 |
decidim-categories | …/decidim-categories | main @ 66919442 |
decidim-extra_home_blocks | …/decidim-extra_home_blocks | main @ b4a41ac4 |
decidim-apartment (multi-tenant) | …/infra/participa-gem | v0.1.0-rc @ 1eb5c0da |
decidim-govbr (esta engine) | local ./decidim-govbr | — (path) |
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
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.
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.
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/.
.gitignore com marcadores de conflitoO .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.
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.