Repositórios criados — explain-diff do ecossistema de gems do Brasil Participativo
Explain-diff · escopo: apenas os repositórios criados

O ecossistema de gems criado pelo Brasil Participativo

Onze repositórios que o time criou — não o app participa, mas as peças que ele monta. O que cada um é, qual é o seu “diff” desde a primeira linha, e como ele se encaixa como gem fixada por revisão no Gemfile.lock.

Repositórios: 11 Commits somados: ~281 Janela: jul/2025 → set/2026 Onde: gitlab.com/lappis-unb/decidimbr · github.com/paulohtfs

1Contexto

O app participa é o palco; estes onze repositórios são o elenco. Antes de olhar cada um, precisamos saber o que é “um módulo Decidim” — e por que o time escolheu criá-los.

1.1 Anatomia de um módulo Decidim

Um módulo (ou “engine”) é, por fora, uma gem Ruby comum; por dentro, é um mini-app Rails embutido no principal. O esqueleto é sempre o mesmo:

  • <nome>.gemspec — a identidade: nome, versão (Decidim::X::VERSION), dependências (decidim-core, etc.).
  • lib/decidim/<nome>/engine.rb — a classe Rails::Engine. É onde o módulo se pendura no Decidim: initializer, config.to_prepare, registro de componentes, ícones, view paths.
  • app/ — cells, controllers, forms, models, views, packs (SCSS/JS) do módulo.
  • db/migrate/ — migrations próprias, quando o módulo tem tabelas.

Um app consome o módulo com uma linha no Gemfile. Quando o módulo ainda não foi publicado em RubyGems, usa-se um ponteiro de git para uma branch, e o Bundler resolve para um commit exato, registrando-o no Gemfile.lock:

gem "decidim-bp_meetings",
    git: "https://gitlab.com/lappis-unb/decidimbr/components-brasil-participativo/decidim-bp_meetings.git",
    branch: "main"

# …o lock congela a resolução:
#   remote: …/decidim-bp_meetings.git
#   revision: 6fe033050ed6e128fb436ef16bd434d106b4acba
Vocabulário

prepend: insere um módulo antes da classe original na cadeia de ancestrais — a sua implementação vence a do core sem editá-la. to_prepare: gancho que roda no boot e a cada reload em dev. Deface: reescreve HTML do core por seletor. content block: um bloco de página (ex.: a home) que o admin liga/desliga.

1.2 O recorte: 11 repositórios

Estes são os repositórios criados e efetivamente usados pelo participa (os únicos com ponteiro git no Gemfile). Todos foram clonados na revisão fixada no lock:

RepositórioGemspec (nome da gem)Revisão fixada
omniauth-govbromniauth-govbr5e9c9282
participa-gemdecidim-apartment1eb5c0da
decidim-bp_templatesdecidim-community_templates4784a016
decidim-module-questionnairesdecidim-questionnairesc778fdc8
decidim-participatory_textdecidim-participatory_textsd4111173
decidim-bp_proposalsdecidim-bp_proposals0447023f
decidim-bp_meetingsdecidim-bp_meetings6fe03305
decidim-bp_commentsdecidim-bp_commentsc5be9a81
decidim-categoriesdecidim-categories66919442
decidim-government-spacesdecidim-government_spacescba43425
decidim-extra_home_blocksdecidim-extra_home_blocksb4a41ac4

Note que nome do repositório e nome da gem divergem em vários casos — bp_templates publica decidim-community_templates; participa-gem publica decidim-apartment. Ao procurar código, vá pelo gemspec.

2Intuição

2.1 Customizar sem fork — a ideia central

O jeito ingênuo de mudar o comportamento de uma dependência é forkar: copiar o repositório do Decidim e editar. O custo aparece depois — a cada release do Decidim, você reconcilia o seu fork com o upstream, para sempre.

O time escolheu o caminho oposto em quase todos os módulos: não forkar; estender em tempo de boot. Um módulo pequeno, instalado por cima, insere suas próprias classes na frente das do core com prepend. O código do core permanece intacto; só o comportamento observado muda.

Cadeia de ancestrais (quem responde primeiro vence) Decidim::BpMeetings::PerStepComments ← nosso prepend Decidim::Meetings::Meeting (core) ApplicationRecord commentable? · accepts_new_comments? → lê a setting da etapa (:step) comportamento original base
O módulo não substitui a classe; ele entra à frente dela. O core continua instalável e atualizável.

Um exemplo concreto, tirado de decidim-bp_meetings. O requisito brasileiro é um toggle positivo por etapa (“Usuário cidadão pode comentar”), mas o core só oferece um toggle negativo global. O módulo resolve assim:

# decidim-bp_meetings/lib/decidim/bp_meetings/features/per_step_comments.rb
# …
# Candidato a upstream: parcial. O toggle positivo por etapa é generalizável,
#   mas a remoção do global da UI é uma decisão BR que upstream provavelmente
#   não aceitaria sem migração extensa.
def self.register_settings
  Decidim.component_registry.find(:meetings)&.tap do |manifest|
    manifest.settings(:step) do |settings|
      settings.attribute :comments_enabled, type: :boolean, default: true
    end
  end
end

Cada feature vive em seu próprio arquivo, com um cabeçalho dizendo a origem do requisito e se aquilo é candidato a virar PR no upstream. Isso transforma “por que isto existe?” em algo legível no próprio código.

2.2 Três naturezas (e um fork)

Ao ler os onze repositórios com atenção, eles se separam em três naturezas — mais uma exceção:

Customização do upstream bp_comments · bp_meetings bp_proposals · questionnaires extra_home_blocks prepend / muta manifest — sem fork Componente/módulo novo categories · government-spaces participatory_text tabela e manifest próprios Integração / infra omniauth-govbr participa-gem (decidim-apartment) ligam o Decidim ao mundo externo Fork do upstream (exceção) bp_templates → community_templates copiado de decidim-ice e portado para 0.32
A maioria estende; um pequeno grupo cria algo novo; um único repositório é de fato um fork.

Por que isso importa? Porque a durabilidade de cada peça é diferente. As customizações sobrevivem a upgrades do Decidim com pouco trabalho. O fork é o que dói: community_templates não tem release compatível com 0.32, e é exatamente por isso que o app mantém o diretório .disabled-for-0.32/.

3Panorama e linha do tempo

Os onze repositórios, com sua natureza, tamanho e janela de vida. A soma é ~281 commits.

RepositórioNaturezaCommitsJanelaDiff em uma frase
decidim-module-questionnairescustomização71mai–set/2026Overrides do decidim-forms: numeração de seções/perguntas, i18n, assets.
participa-geminfra56ago/2025–ago/2026Apartment para Decidim: schema por organização + elevator por host.
decidim-bp_templatesfork39set/2025–ago/2026Fork do community_templates, portado para Decidim 0.32.1.
decidim-participatory_textcomponente28abr–ago/2026Componente de texto participativo com edição por parágrafo (Hotwire).
decidim-extra_home_blockscustomização20jun–ago/2026Enhancements opt-in a content blocks do core (timeline, hero).
decidim-government-spacesmódulo novo17jun–ago/2026Hierarquia Órgão→Setor com admin escopado (sem admin: true).
decidim-bp_meetingscustomização14mai–ago/2026Criação, comentários e anexos controlados por etapa.
decidim-bp_commentscustomização13jun–set/2026Respostas oficiais (só admin), árvore de 2 níveis, limite de 5 min, moderação.
decidim-bp_proposalscustomização10mai–ago/2026Comentários por etapa; esconde setting de textos participativos.
omniauth-govbrintegração8jul–dez/2025Estratégia OmniAuth OAuth2/PKCE do gov.br.
decidim-categoriesmódulo novo5jun–ago/2026Categorias hierárquicas próprias (decidim_categories_categories).
2025-07 10 2026-01 04 07 09 omniauth-govbr participa-gem bp_templates participatory_text questionnaires bp_proposals bp_meetings bp_comments categories government-spaces extra_home_blocks Onda 1: fundação (identidade + multi-tenant + templates) Onda 2: customização de componentes (bp_*) e novos módulos
Duas ondas: a fundação (jul/2025–…) e a onda de customização/módulos (abr/2026 em diante).

4Os diffs, repo a repo

Para cada repositório: o que é, qual o arco do seu próprio histórico (o “diff” da primeira linha ao commit fixado), e os arquivos que importam.

4.1 Customizações do upstream (sem fork)

Estes cinco estendem módulos do core do Decidim. Nenhum tem tabela própria; todos usam prepend/include e mutação de manifest.

decidim-bp_meetings customização

Diff (14 commits, mai→ago/2026): nasceu em mai/2026 com o padrão de features isoladas; ganhou per_step_creation, per_step_comments e anexos por etapa; terminou com o bump para 0.32.

Arquivos: lib/decidim/bp_meetings/features/{per_step_creation,per_step_comments,per_step_attachments}.rb

Marco: feat: oculta toggle global de criação de reunião (criação só por etapa).

decidim-bp_comments customização

Diff (13 commits, jun→set/2026): o mais “político” dos módulos. Respostas oficiais (só perfis administrativos respondem), árvore de dois níveis, etiqueta de resposta oficial e limite de 5 minutos para editar/excluir. No fim, passa a considerar Admin de Órgão/Setor na moderação.

Arquivos: engine.rb (prepends sobre Decidim::Comments::*), component_settings.rb.

# engine.rb — todos os overrides são prepend sobre o core, sem fork
initializer "decidim_bp_comments.overrides" do
  config.to_prepare do
    Decidim::Comments::Permissions.prepend(Decidim::BpComments::CommentsPermissionsOverride)
    Decidim::Comments::CommentForm.prepend(Decidim::BpComments::CommentFormOverride)
    Decidim::Comments::CommentCell.prepend(Decidim::BpComments::CommentCellOverride)
    # `include` e não `prepend`: o módulo só acrescenta associações de anexos.
    Decidim::Comments::Comment.include(Decidim::BpComments::CommentAttachmentsOverride)
  end
end

decidim-bp_proposals customização

Diff (10 commits, mai→ago/2026): o menor do conjunto. per_step_comments em propostas e rascunhos colaborativos; esconde o setting nativo de textos participativos; reordena o checkbox de comentários.

Arquivos: lib/decidim/bp_proposals/features/per_step_comments.rb

decidim-module-questionnaires customização

Diff (71 commits, mai→set/2026 — o maior): trabalha sobre o decidim-forms (a engine de questionários). Sem manifest novo — tudo via config.to_prepare. Numerou seções e perguntas (no formulário, na leitura e na exportação), trouxe a tradução pt-BR e corrigiu assets na edição de respostas.

Arquivos: lib/decidim/questionnaires/engine.rb; app/ com cells, presenters, serializers e overrides.

decidim-extra_home_blocks customização (opt-in)

Diff (20 commits, jun→ago/2026): enhancements opt-in a content blocks do core — timeline de etapas inline, correção do hero dos espaços participativos. Cada feature vira uma setting com default no comportamento original.

# engine.rb — não re-registra o bloco; muta o manifest já registrado
initializer "decidim_extra_home_blocks.extend_extra_data_content_block",
            after: "decidim_participatory_processes.content_blocks" do
  Decidim::ExtraHomeBlocks.extend_content_block(:participatory_process_homepage, :extra_data) do |manifest|
    manifest.cell = "decidim/extra_home_blocks/content_blocks/extra_data"
    manifest.settings do |settings|
      settings.attribute :timeline_display, type: :enum, default: "inline", choices: %w(original inline)
    end
  end
end

4.2 Componentes/módulos novos

Aqui o time não estendeu o core — criou estrutura própria (tabelas, manifest, admin).

decidim-categories módulo novo

Diff (5 commits, jun→ago/2026): o menor em commits, mas não em ambição. Introduz uma tabela própria (decidim_categories_categories) com hierarquia infinita (parent_id) e escopo por espaço participativo — em vez de usar as categorias nativas do Decidim. Traz overrides para Proposals, Meetings e votos.

Arquivos: lib/decidim/categories/{component,engine,admin_engine}.rb, overrides/{proposals,meetings,proposals_votes}_overrides.rb.

decidim-government-spaces módulo novo

Diff (17 commits, jun→ago/2026): controle de acesso por hierarquia de governo Órgão → Setor. Um super admin monta a hierarquia e convida admins escopados; estes administram só os processos alcançáveis pelas suas memberships e nunca são admin: true. Acesso recalculado ao vivo → revogar uma membership revoga o acesso na hora.

Arquivos: lib/decidim/government_spaces/participatory_processes/{space_assignment,role_config_extensions,update_process_extensions}.rb.

decidim-participatory_text componente

Diff (28 commits, abr→ago/2026): um componente de texto participativo com edição colaborativa por parágrafo, usando Hotwire. Tem API GraphQL própria (paragraph_type), admin engine e engine própria.

Arquivos: lib/decidim/participatory_texts/{component,engine,admin_engine,api}.rb, lib/decidim/api/paragraph_type.rb.

4.3 Integração e infraestrutura

omniauth-govbr integração

Diff (8 commits, jul→dez/2025): uma estratégia OmniAuth do zero para o gov.br. Define os escopos (openid email profile govbr_confiabilidades), liga PKCE, e mapeia uid → sub e o cpf no bloco info. Depois ajusta o nickname (usa name, não sub) e remove o cpf do escopo.

# lib/omniauth/strategies/govbr.rb
option :pkce, true
option :grant_type, 'authorization_code'
option :client_options, { site: 'https://sso.staging.acesso.gov.br',
                          authorize_url: '/authorize', token_url: '/token',
                          user_info_url: '/userinfo' }
uid { raw_info['sub'] }
info do
  { sub: raw_info['sub'], name: raw_info['name'], nickname: nickname,
    email: raw_info['email'], cpf: raw_info['cpf']&.gsub(/\D/, "") }
end

participa-gem (decidim-apartment) infra

Diff (56 commits, ago/2025–ago/2026): a peça que torna o multi-tenant possível. Integra o apartment ao Decidim: um elevator que resolve o schema pelo host, o /system fixo em public, middlewares de tenant para Sidekiq e ActiveJob, e overrides de seeds/dashboard.

# lib/decidim/apartment/elevator.rb — resolve o tenant por request
def parse_tenant_name(request)
  host = request.host
  return tenant_name_for_system(request) if request.path.start_with?("/system")
  distribution_key = Decidim::Apartment::DistributionKey.find_by(host: host)
  return "public" unless distribution_key
  distribution_key.key
end

Detalhe de valor: DistributionKey é o mapa host→schema. Sem ele, um host desconhecido cai em public — falha segura.

4.4 O fork

decidim-bp_templates (decidim-community_templates) fork

Origem: fork de decidim-ice/decidim-module-community_templates (autores Ivan Vergés e Hadrien Froger) — o único repositório que é de fato uma cópia evoluída, não uma extensão.

Diff (39 commits, set/2025→ago/2026): funciona como upstream próprio por meses (export/import de questionários, normalização de locales, transações git) e então, em ago/2026, é portado para Decidim 0.32.1 (port: make the module run on Decidim 0.32.1, ajuste de @use do Sass e mount do admin engine no escopo de locale).

Por que o fork dói

É o módulo que obriga o app a carregar .disabled-for-0.32/: features que dependem dele não têm equivalente no 0.32 e ficam paradas até haver release compatível. Todo o resto do ecossistema sobreviveu ao upgrade sem esse custo.

5Padrões transversais

Lendo os onze juntos, alguns hábitos do time ficam evidentes — e são eles que explicam por que o ecossistema aguentou o salto para 0.32.

Cabeçalho de feature

Cada feature declara origem (issue/CA), motivo e candidatura a upstream no topo do arquivo. Intenção explícita, versionada.

Opt-in com default seguro

Enhancements entram como setting com default no comportamento original — ligar é escolha do admin, não surpresa.

prepend > fork

Nove dos onze estendem o core sem copiá-lo. Só community_templates é fork — e é o mais custoso de atualizar.

A onda “bump 32”

Entre ago e set/2026 quase todos receberam um merge core/bump-32 / core: bump to 32. Foi um esforço coordenado de upgrade.

Sem shared lib

Os bp_* são deliberadamente independentes (“sem shared lib”) — cada componente carrega a sua própria customização.

i18n pt-BR central

Tradução faltante e grafia pt-BR aparecem como tema recorrente dentro dos módulos, não só no app.

6Como plugam no participa

Nenhum destes módulos é publicado em RubyGems. Todos entram por git, e o Gemfile.lock congela a revisão exata — o que torna o app reprodutível, mas também cria um ponto único de verdade:

PonteiroOnde resolveConsequência
branch: "main"10 dos 11A branch avança; só o commit no lock te protege.
branch: "core/bump-32"bp_templatesBranch temporária de upgrade — sinal de que o port é recente.
branch: "v0.1.0-rc"participa-gemMulti-tenant ainda em pré-release.
Regra de ouro ao investigar

Um commit bump: ou core: update modules no app muda comportamento sem nenhum diff local. Antes de culpar o participa, confirme a revisão no Gemfile.lock e leia o módulo naquele commit.

7Armadilhas

Acesso e procedência

Os repositórios vivem sob gitlab.com/lappis-unb/decidimbr (a maioria) e github.com/paulohtfs/omniauth-govbr. Foram alcançáveis anonimamente para o clone, mas trate-os como dependências de terceiros: a revisão pinada é a fonte, não a branch.

Divergência do fork

community_templates segue o upstream decidim-ice por conta própria. Sem um release compatível com 0.32, é o elo fraco do ecossistema — é o que sustenta o .disabled-for-0.32/.

Nomes divergentes

Repositório ≠ gem em vários casos (bp_templatesdecidim-community_templates; participa-gemdecidim-apartment). Ao buscar, vá pelo gemspec, não pela pasta.

8Como ler isto

A seção 2 dá a ideia (customizar sem fork); a 3 dá o mapa; a 4 dá o detalhe por repositório; a 5–6 dão os padrões e o encaixe no app. Este documento é o complemento de docs/participa-roadmap.html — aquele cobre o app, este cobre os repositórios criados.

Origem dos dados

Tudo foi extraído dos clones reais feitos em participa-gems/, cada um na revisão fixada no Gemfile.lock: git log, gemspec, README e arquivos de lib/. Somente community_templates foi classificado como fork a partir do homepage/autoria do seu gemspec.

Repositórios criados — explain-diff · documento gerado a partir dos 11 clones em participa-gems/. Fonte editável: docs/participa-gems.html.