diff --git a/docs/plans/c147-sync-google-calendar-producao.md b/docs/plans/c147-sync-google-calendar-producao.md
new file mode 100644
index 00000000..7d86818e
--- /dev/null
+++ b/docs/plans/c147-sync-google-calendar-producao.md
@@ -0,0 +1,82 @@
+# Google Calendar: sync configurado em produção não funciona (diagnóstico + fix)
+
+Status: rascunho
+Atualizado em: 2026-08-27
+Issue: #932
+Priority: P1
+Model: composer-2.5
+Impeccable: A — N/A (sem UI nova; a pill/dialog de estado já existem)
+Rascunho UI: N/A — sem UI
+Appetite: ~1–2 dias eng; um outcome verificável — com a integração configurada no Google e no Teqo, criar/editar uma atividade reflete no calendário em minutos e o estado do sync é honesto (nunca silenciosamente quebrado)
+Responsável: —
+
+## Intenção
+
+"Eu configurei a integração do Google Calendar para a agenda da campanha em produção, mas ela não funciona — não está sincronizando. Descubra por que e conserte." A integração foi entregue (motor de reconciliação bidirecional, pill de estado, webhook), mas na prática a agenda oficial da campanha e o Google Calendar não estão conversando — e nada na interface diz por que. O problema não é "falta feature": é uma integração existente que falha em silêncio. Este item existe para (1) diagnosticar a causa em produção, (2) consertá-la e (3) garantir que, daqui em diante, uma falha de sync nunca passe despercebido — o estado mostrado na agenda precisa ser honesto, inclusive quando o canal de push do Google expira sem ninguém perceber.
+
+## Persona e fluxo
+
+- **Persona / contexto:** o coordenador (ou o próprio deputado) que mantém `/campanha/agenda` como fonte da agenda oficial; já configurou a service account no Google Cloud e o calendário no Teqo, e confia que "configurado" significa "funcionando".
+- **Job principal:** criar/editar uma atividade no Teqo e vê-la aparecer no calendário do Google em minutos — ou, se não aparecer, saber imediatamente o motivo na própria agenda.
+- **Fluxo desejado:** configura a integração uma vez → usa a agenda normalmente → quando algo quebra (credencial revogada, canal de push expirado, calendarId errado), a pill de estado diz o que aconteceu em vez de fingir que está tudo bem → o coordenador (ou o runbook) resolve sem depender de sorte ou de uma passada de sync que ninguém disparou.
+- **Anti-goals de produto:** não virar um projeto de OAuth de usuário (troca de modelo de credencial), não redesenhar os modais de estado, não reconstruir o motor de sync que já existe e funciona em dev.
+
+## Objetivo e aceite
+
+- Com a integração configurada no Google e no Teqo, criar ou editar uma atividade em `/campanha/atividades` reflete no calendário do Google em minutos.
+- O estado do sync exibido na agenda (pill + dialog existentes) é honesto: se o sync está quebrado, ele diz que está quebrado e por quê — nunca "sincronizado" quando não está, nunca silêncio.
+- Se o canal de push do Google expirar e nenhuma passada de sync rodar para renová-lo, existe um caminho de renovação que não depende de sorte (hipótese a validar no diagnóstico).
+- Guardrails: o Teqo permanece a fonte da verdade — uma falha do Google nunca corrompe nem bloqueia os dados de atividade no Teqo; a integração continua opcional e fail-closed (sem credencial → agenda funciona normal, estado "não configurado"); o lockdown de liderança e o modelo de municípios ficam intocados.
+
+## Dados (intenção)
+
+- **Vou apresentar dados?** Não — este item não cria superfície de dados nova; usa o estado já registrado no doc de sync (lastError, lastErrorAt, pushChannelError) como insumo do diagnóstico.
+- **Decisões desbloqueadas:** coordenador/operador decide, ao ler o estado honesto, se o problema é configuração no Google, env no servidor ou canal de push — sem precisar abrir o banco.
+- **Forma:** adiada ao plano de implementação — aqui só a restrição: o estado precisa ser legível por um humano não-técnico na UI existente.
+
+## Dados da decisão (literais)
+
+- `GOOGLE_CALENDAR_SERVICE_ACCOUNT_KEY` — base64 do JSON da service account GCP (conforme `.env.example:61–67: "cat key.json | base64 -w0"`; a service account precisa de permissão "make changes to events" no calendário da campanha). Em produção, o valor vive em `~/stack/teqo-1313.env` no homeserver.
+- Comportamento fail-closed já travado: env ausente/malformada → credencial `null` → status derivado `not-configured` → agenda do Teqo segue funcionando sem espelho no Google; nenhum erro além da pill.
+- O `calendarId` é configurado por admin no doc único da collection de sync (campo admin-only); um valor errado aqui é causa plausível de "configurado mas não sincroniza".
+
+## Direção no codebase (hipótese)
+
+- **Áreas prováveis:** `src/utilities/googleCalendarSync.ts` (motor de reconciliação), `src/utilities/googleCalendarClient.ts` (JWT via `jose`, timeout 15s/hop), `src/utilities/googleCalendarSyncHooks.ts` (hooks afterChange/afterDelete de `activity`, budget de 5s — C114-LOCK), `src/app/(campaign)/campanha/agenda/google-webhook/[secret]/route.ts` (push), `src/components/campaign/agenda/AgendaGoogleSyncChrome.tsx` + `GoogleCalendarSyncDialog.tsx` (estado exibido), `src/collections/GoogleCalendarSync.ts` (doc único, `calendarId`).
+- **Hipóteses de causa-raiz a testar, em ordem de barato→caro:** env ausente no container de produção (fail-closed engoliu o problema); service account sem compartilhamento no calendário ou credencial revogada; `calendarId` errado; canal de push expirado (30 dias) sem passada que o renove; webhook inalcançável atrás do túnel Cloudflare (`getCampaignInviteBaseURL`/`NEXT_PUBLIC_SITE_URL`).
+- **Precedente a olhar:** docs/plans/c114-google-calendar-push-agenda-oficial.md (+ -impl), c115 (bidirecional), c114-lock (budget de hooks), c122; Issues #635/#636 (closed).
+- **Risco de acoplamento:** o budget de 5s dos hooks (C114-LOCK) existe para o write path nunca esperar o Google — o guardrail "falha não é silenciosa" deve vir de telemetria/estado, não de deixar o hook lento; não reconstruir o motor, não trocar o modelo de credencial (C149 é outro item).
+
+## Dependências
+
+- Nenhuma dura. C114/C115 (entregues, closed) são pré-condição de código — já estão em `main`/produção.
+
+## Fora de escopo
+
+- C149 — troca do modelo de credencial para OAuth de usuário (outro item; aqui a service account permanece).
+- C148 — layout/redesign dos modais de estado (aqui só se garante que o conteúdo do estado é honesto).
+- Reconstrução do motor de reconciliação bidirecional; introdução de cron/queue como infra nova (só se o diagnóstico provar ser a causa-raiz mínima).
+
+## Rabbit holes de produto
+
+- **"Já que vou tocar no sync, implemento OAuth com conta de usuário do Google."** Se alguém "só completar": reescreve o modelo de credencial inteiro, atrasa o fix de um bug P1. **Corte neste item:** service account permanece; troca é C149.
+- **"Aproveito e redesenho a pill/dialog do sync."** Se alguém "só completar": projeto de UI para mascarar um problema de backend. **Corte neste item:** estado honesto no conteúdo; forma é C148.
+- **"Diagnóstico virou auditoria do motor inteiro + suíte de e2e de sync."** Se alguém "só completar": semana de engenharia para um outcome de 1–2 dias. **Corte neste item:** diagnóstico orientado pelo checklist (env → credencial → calendarId → estado do doc/pushChannelError → canal de push → webhook), fix da causa-raiz e o guardrail de não-silêncio.
+
+## Questões em aberto (produto)
+
+- **O fix pode incluir operação no homeserver (p. ex. env faltando em `~/stack/teqo-1313.env`, reinício de container)?** **Opções:** A) sim, e o que for configuração vira linha no runbook; B) só código, operação fica com humano. **Recomendação:** A — se a causa-raiz for configuração, registrar o passo no runbook `docs/ops/teqo-1313-deploy.md` para não depender de memória. _(assumido — validar com produto)_
+- **Quer telemetria mínima no log do servidor quando o hook de sync aborta (hoje é `logger.debug`/estado no doc, invisível no write path)?** **Opções:** A) log de erro estruturado no abort; B) manter só estado no doc. **Recomendação:** A — a falha silenciosa foi exatamente o sintoma relatado; log com nível de erro no abort é a mudança mínima que fecha a classe do problema.
+- **A renovação do canal de push precisa de gatilho garantido (a cada passada vira "renova só se algo disparar passada")?** **Opções:** A) renovação atrelada a um gatilho que comprovadamente roda (a auto-retry no load da agenda, se diagnosticada como suficiente); B) infra mínima de cron. **Recomendação:** A — cron/queue só se o diagnóstico provar que nenhum gatilho existente roda com frequência suficiente; decidir depois do diagnóstico, não antes.
+
+## Referências
+
+- GitHub Issue: #— (a registrar via `pnpm agent:register`)
+- `src/utilities/googleCalendarSync.ts`, `src/utilities/googleCalendarClient.ts`, `src/utilities/googleCalendarSyncHooks.ts`
+- `src/collections/GoogleCalendarSync.ts`, `src/collections/Activity.ts`
+- `src/app/(campaign)/campanha/agenda/google-webhook/[secret]/route.ts`
+- `src/components/campaign/agenda/AgendaGoogleSyncChrome.tsx`, `GoogleCalendarSyncDialog.tsx`
+- `.env.example:61–67` — literal e contrato fail-closed do env
+- docs/plans/c114-google-calendar-push-agenda-oficial.md (+ -impl), docs/plans/c115-google-calendar-edicao-bidirecional.md (+ -impl), docs/plans/c114-lock-hooks-do-sync-do-google-calendar-seguram-impl.md
+- `docs/ops/teqo-1313-deploy.md` — runbook do homeserver (destino das conclusões de configuração)
+- `AGENTS.md` — produção = homeserver (`teqo_1313`), env em `~/stack/teqo-1313.env`, sem Vercel
diff --git a/docs/plans/c148-modais-agenda-desktop-ui-draft.html b/docs/plans/c148-modais-agenda-desktop-ui-draft.html
new file mode 100644
index 00000000..8e570bd5
--- /dev/null
+++ b/docs/plans/c148-modais-agenda-desktop-ui-draft.html
@@ -0,0 +1,376 @@
+
+
+
+
+
+
+ UI draft — C148 · Modais da agenda no desktop
+
+
+
+
C148 · Modais da agenda usáveis no desktop
+
+ Assessor/coordenador no desktop, criando atividade e sincronizando a agenda. Zinc neutro +
+ um acento (indigo) só no CTA primário. Estático — só medidas e encaixe.
+
+
+
+
+
+ Cena 1 · Desktop ~1280px — modal de criação de atividade
+
+
+ Largura generosa, paddings contidos, Início/Término lado a lado (cada um com data+hora na
+ mesma linha), corpo com rolagem interna e rodapé fixo.
+
+
+
+
+
+
+
+
+
+
Nova atividade
+ ×
+
+
+
+
+ Título
+
+ Título da atividade
+
+
+
+
+ Tipo
+
+ Selecione o tipo▾
+
+
+
+ Status
+
+ Selecione o status▾
+
+
+
+
+
+ Município
+
+ Selecione o município▾
+
+
+
+ Localidade
+
+ Bairro, praça ou referência
+
+
+
+
+
+ Início
+
+
+ dd/mm/aaaa
+
+
+ --▾
+
+
+ --▾
+
+
+
+
+ Término
+
+
+ dd/mm/aaaa
+
+
+ --▾
+
+
+ --▾
+
+
+
+
+
+ Assessores responsáveis
+
+
+
+
+ Adicionar
+
+
+
+
+ Organizações apoiadoras
+
+
+
+
+
+
+
+
+
+
+ Cancelar
+
+
+ Salvar atividade
+
+
+
+
+
+
+
+
+
+
+ Cena 2 · Desktop — modal de sincronização Google (feed ICS segue o mesmo padrão)
+
+
+ Corpo com altura máxima e rolagem interna: a lista longa de opções não vaza para fora da
+ tela; os botões ficam fixos no rodapé.
+
+
+
+
+
+
Sincronizar com Google Agenda
+ ×
+
+
+
+ Escolha o que importar e o que publicar no feed da agenda. As opções continuam
+ acessíveis rolando dentro do modal.
+
+
+
+
+
+
Importar eventos do Google
+
+
+
+
+
+
+
Publicar atividades no Google Agenda
+
+
+
+
+
+
+
Selecionar quais calendários sincronizar
+
+
+
+
+
+
+
Avisos de conflito de horário
+
+
+
+
+
+
+
Fuso horário dos eventos
+
+
+
+
+
+
+
Reprocessar sincronização anterior
+
+
+
+
+
+
+
+ Fechar
+
+
+ Sincronizar agora
+
+
+
+
+
+
+
+
+
+
+ Cena 3 · Mobile ~390px — o mesmo modal de criação (não muda)
+
+
+ Hoje o mobile já abre como folha (Drawer) com corpo rolável e campos empilhados; a
+ correção desktop não pode alterar isso.
+
+
+
+
+
+
+
+
+
+
Nova atividade
+ ×
+
+
+
+
+ Título
+
+ Título da atividade
+
+
+
+ Tipo
+
+ Selecione o tipo▾
+
+
+
+ Município
+
+ Selecione o município▾
+
+
+
+ Início
+
+ dd/mm/aaaa
+
+
+
+ Hora▾
+
+
+ Min▾
+
+
+
+
+ Término
+
+ dd/mm/aaaa
+
+
+
+ Hora▾
+
+
+ Min▾
+
+
+
+
+ Assessores responsáveis
+
+
+
+
+
+
+
+
+
+ Cancelar
+
+
+ Salvar
+
+
+
+
+
+
+
+
diff --git a/docs/plans/c148-modais-agenda-desktop.md b/docs/plans/c148-modais-agenda-desktop.md
new file mode 100644
index 00000000..385067e5
--- /dev/null
+++ b/docs/plans/c148-modais-agenda-desktop.md
@@ -0,0 +1,93 @@
+# Agenda: modais usáveis no desktop (criação de atividade, sync e feed)
+
+Status: rascunho
+Atualizado em: 2026-08-27
+Issue: #933
+Priority: P2
+Model: composer-2.5
+Impeccable: B — encaixe nos modais da agenda
+Rascunho UI: docs/plans/c148-modais-agenda-desktop-ui-draft.html
+Appetite: ~0,5–1 dia eng; um outcome verificável — no desktop, os três modais da agenda abrem com largura/padding coerentes, conteúdo inteiro acessível (scroll quando precisa) e o par data/hora não empilha
+Responsável: —
+
+## Intenção
+
+Dois pedidos fundidos sobre `/campanha/atividades` no desktop. No modal de criação de atividade: "o layout no desktop está esquisito — bordas gigantes, modal desnecessariamente estreito, e o seletor de data/hora fica um em cima do outro". No modal de sincronização Google: "o modal é estreito demais e o conteúdo passa do topo e da base da tela, sem rolagem para ver o resto". É a mesma dor — modais da agenda que não respeitam a janela do desktop — e o modal de feed ICS carrega o mesmo defeito latente do sync. Correção de encaixe, não redesign.
+
+## Persona e fluxo
+
+- **Persona / contexto:** assessor (ou coordenador) na mesa, no desktop, montando a agenda da semana e sincronizando o Google Calendar; tarefa operacional curta e repetida.
+- **Job principal:** criar/editar uma atividade e configurar a sincronização sem lutar contra o modal.
+- **Fluxo desejado:**
+ 1. Abre "Nova atividade" → modal abre com largura generosa e paddings contidos (nada de "bordas gigantes").
+ 2. Em Início/Término, os dois campos ficam lado a lado, cada um com data e hora na mesma linha.
+ 3. Se o conteúdo excede a altura da janela, o corpo rola internamente — os botões de ação ficam sempre visíveis no rodapé.
+ 4. Abre o modal de sync Google → a lista de opções rola no corpo, footer fixo.
+ 5. Abre o modal de feed ICS → mesmo comportamento de corpo rolável.
+ 6. No mobile, nada muda (folha com rolagem de hoje).
+- **Anti-goals de produto:** redesign de formulário/campos, mudança de lógica (validação/salvamento) dos modais, padronização de outros modais do app, feature nova.
+
+### Esboço de fluxo (B/C/D)
+
+```text
+[/campanha/atividades] → [Nova atividade · modal desktop] → preencher → Início/Término lado a lado → [Salvar]
+ → [Sync Google · modal] → corpo com rolagem + footer fixo → [Sincronizar]
+ → [Feed ICS · modal] → corpo com rolagem + footer fixo → [Copiar link]
+```
+
+### Rascunho UI (B/C/D)
+
+- Rascunho UI (gate): `docs/plans/c148-modais-agenda-desktop-ui-draft.html` — cena 1 (criação em desktop ~1280px), cena 2 (sync Google com corpo rolável e footer fixo; feed ICS segue o mesmo padrão), cena 3 (a mesma criação em ~390px, mobile inalterado).
+
+## Objetivo e aceite
+
+- No desktop, os TRÊS modais da agenda (criação/edição de atividade, sync Google, feed ICS) abrem com largura e padding coerentes — sem bordas gigantes, sem largura espremida.
+- Nenhum dos três corta conteúdo: quando o corpo excede a altura disponível, há rolagem interna e o rodapé de ações permanece visível.
+- O par Início/Término do modal de criação não empilha no desktop — data e hora de cada campo na mesma linha (o rascunho mostra a disposição recomendada).
+- Guardrails: mobile dos três modais intocado (apenas não pode regredir); lógica de validação/salvamento e dados intocados; nenhum outro modal do app muda.
+
+## Dados (intenção)
+
+- **Vou apresentar dados?** Não — correção de layout/encaixe; nenhuma superfície de dados nova.
+- **Decisões desbloqueadas:** nenhuma além do aceite visual do gate.
+
+## Dados da decisão (literais)
+
+- N/A — a intenção não fixa IDs, strings ou thresholds; os literais operacionais são os três modais nomeados na direção e o corte desktop vs mobile (mobile = comportamento atual da folha com rolagem).
+
+## Direção no codebase (hipótese)
+
+- **Áreas prováveis:** `src/components/campaign/activity/` — `ActivityOverlay.tsx` (criação/edição), `GoogleCalendarSyncDialog.tsx`, `CalendarFeedDialog.tsx`, `ActivityDateTimeField.tsx`; base compartilhada `src/components/ui/dialog.tsx`.
+- **Hipótese central:** o override desktop do overlay usa largura/padding não-responsivos (`max-w-2xl`, `p-0`) que NÃO derrubam as variantes `sm:` da base (`sm:max-w-md`, `sm:p-8`) — em telas ≥ sm vencem as da base, e o modal fica com 28rem de largura e 2rem de padding (o "estreito + bordas gigantes"). Nos dois dialogs menores falta `max-h` + rolagem no caminho desktop (o mobile já resolve com a folha). O empilhamento data/hora vem da caixa estreita + grade de duas colunas.
+- **Precedente a olhar:** o padrão mobile do próprio overlay (folha com max-h e corpo rolável) e do sync/feed no mobile — é o padrão a replicar no desktop.
+- **Risco de acoplamento:** `ui/dialog.tsx` é a base de todos os modais do app — mudá-la globalmente causaria regressão silenciosa em toda tela; preferir correção local nos três modais da agenda (override explícito por breakpoint), sem tocar em outros consumidores.
+
+## Dependências
+
+- Nenhuma dura. Soft: C123 (ActivityOverlay — superfície de criação atual) e C94 (feed ICS) como precedentes de onde o defeito vive.
+
+## Fora de escopo
+
+- Mobile dos três modais (já está bom) — apenas garantir que não regrida.
+- Lógica dos modais: validação, salvamento, dados e estados de erro intocados.
+- Redesenho do formulário de atividade (campos, defaults, seções) — outro dia, outro pedido.
+- Padronização de outros modais do app fora da agenda.
+- A funcionalidade de sync/feed em si (feature existente — só o encaixe).
+
+## Rabbit holes de produto
+
+- **"Aproveitar e padronizar todos os modais do app."** Se alguém "só completar": varredura de todas as telas, PR gigante, regressão em massa. **Corte neste item:** só os três modais da agenda.
+- **"Redesenhar o formulário de atividade."** Se alguém "só completar": reorganizar seções, campos novos, defaults. **Corte:** só encaixe/medidas dos campos existentes.
+- **"Mudar a base ui/dialog.tsx para resolver de uma vez."** Se alguém "só completar": mudança global sem varredura de consumidores. **Corte:** mudança local nos três modais; a base só muda se o executor varrer os consumidores e provar zero regressão.
+
+## Questões em aberto (produto)
+
+- **Como dispor Início/Término no desktop?** **Opções:** A) os dois campos lado a lado, cada um com data+hora inline | B) um campo por linha com data+hora na mesma linha. **Recomendação:** A no desktop (aproveita a largura recuperada), fallback B se apertado. _(assumido — validar no gate com o rascunho; a cena 1 mostra A)._
+- **Largura dos modais de sync/feed:** manter a atual (~32rem) ou ampliar um pouco (~36rem)? **Recomendação:** ampliar pouco e garantir rolagem — o defeito principal do sync/feed é a falta de scroll, não a largura. _(assumido — a cena 2 mostra ~36rem)._
+
+## Referências
+
+- GitHub Issue: — (após `pnpm agent:register`)
+- Rascunho UI (gate): `docs/plans/c148-modais-agenda-desktop-ui-draft.html`
+- Arquivos para abrir primeiro: `src/components/campaign/activity/ActivityOverlay.tsx`, `GoogleCalendarSyncDialog.tsx`, `CalendarFeedDialog.tsx`, `ActivityDateTimeField.tsx`, `src/components/ui/dialog.tsx`
+- `AGENTS-campaign.md` — seção "Campaign activities (C3)" (vocabulário Atividade; dado interno de staff, sem LGPD/consent aqui)
diff --git a/docs/plans/c149-conectar-google-oauth-ui-draft.html b/docs/plans/c149-conectar-google-oauth-ui-draft.html
new file mode 100644
index 00000000..05d8aba3
--- /dev/null
+++ b/docs/plans/c149-conectar-google-oauth-ui-draft.html
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+ UI draft — C149 Conectar Google (OAuth)
+
+
+
+
+ Candidato/coordenador configura o espelho Google Calendar da agenda — conexão por botão, sem
+ service account. Cenas: não configurado, conectado, erro de token, mobile.
+
+
+
+
+ Cena 1 · Desktop — estado: não configurado
+
+
+
+
+
Google Calendar
+
+ Espelha a agenda da campanha num calendário Google, com atualização em minutos.
+
+
+
+ Não configurado
+
+
+
+
+ Conecte a conta Google do calendário da campanha para ativar o espelho.
+
+
+ Você será levado à tela de consentimento do Google para autorizar o Teqo a gerenciar o
+ calendário da campanha.
+
+
+
+
+ A configuração manual por service account continua disponível como caminho técnico de
+ fallback.
+
+
+
+
+
+
+ Cena 2 · Desktop — estado: conectado
+
+
+
+
+
Google Calendar
+
+ O espelho usa a conexão abaixo para escrever os compromissos no calendário da
+ campanha.
+
+
+
+ Conectado
+
+
+
+
+
+
+
Conta Google da campanha
+
+
+
+
+
+
+
+
+
+ Desconectar revoga o acesso do Teqo; depois, revogue o app nas configurações de
+ segurança da conta Google.
+
+
+
+
+
+
+ Cena 3 · Desktop — estado: erro de token (caminho de reconexão)
+
+
+
+
+
Google Calendar
+
+ O espelho está pausado até a conexão ser restabelecida.
+
+
+
+ Erro
+
+
+
+
Erro de conexão com o Google
+
+ O token de acesso não pôde ser renovado. O Teqo continua sendo a fonte da verdade — a
+ agenda segue funcionando aqui; nada foi perdido no espelho.
+
+
+ Reconecte com a mesma conta Google do calendário da campanha. Se a conta mudou,
+ desconecte antes e conecte novamente.
+
+
+
+
+
+
+
+
+
+
+
+ Cena 4 · Mobile ~390px — estado: não configurado (cena 1)
+
+
+
+
+
+
Google Calendar
+
+ Espelha a agenda da campanha num calendário Google.
+
+
+
+ Não configurado
+
+
+
+
+ Conecte a conta Google do calendário da campanha para ativar o espelho.
+
+
+ Você autoriza o Teqo na tela de consentimento do Google.
+
+
+
+
+ Service account segue disponível como fallback técnico.
+
+
+
+
+
+
+
diff --git a/docs/plans/c149-conectar-google-oauth.md b/docs/plans/c149-conectar-google-oauth.md
new file mode 100644
index 00000000..f17cfd84
--- /dev/null
+++ b/docs/plans/c149-conectar-google-oauth.md
@@ -0,0 +1,91 @@
+# C149 — Google Calendar: botão Conectar Google (OAuth) substitui a configuração manual de service account
+
+Status: rascunho
+Atualizado em: 2026-08-27
+Issue: #934
+Priority: P2
+Model: composer-2.5
+Depends: #932 (C147)
+Impeccable: B — encaixe na agenda (configuração do sync)
+Rascunho UI: docs/plans/c149-conectar-google-oauth-ui-draft.html
+Appetite: ~2 dias eng; um outcome verificável — o candidato/coordenador conecta a conta Google do calendário da campanha clicando um botão (consent do Google), sem criar service account nem colar JSON
+Responsável: —
+
+## Intenção
+
+Ativar o espelho Google Calendar (C114) hoje exige cerimônia de engenharia: criar service account no GCP, baixar JSON, base64, colar em env, compartilhar o calendário com o e-mail da conta de serviço. O dono do produto: _"This manual configuration of the service account plus calendar isn't ideal — isn't there a way to expose a button that allows the candidate to sync a calendar to it?"_ O registro C114 recusou OAuth **por pessoa** (N staff escrevendo no calendário pessoal de cada um). O pedido atual é diferente: **uma** concessão OAuth em que candidato/coordenador autoriza o Teqo a gerenciar **o calendário da campanha** — variante não coberta pelo registro, vinda como nova evidência do dono. Configuração vira um clique.
+
+## Persona e fluxo
+
+- **Persona / contexto:** candidato ou coordenador geral, no computador (mesa), configurando o espelho da agenda uma única vez — e voltando só quando algo quebra.
+- **Job principal:** "conecto a conta Google do calendário da campanha ao Teqo sem ajuda de engenharia".
+- **Fluxo desejado:** na configuração do sync da agenda → clica "Conectar com o Google" → tela de consent do Google (escolhe a conta da campanha) → volta ao Teqo com estado "conectado" → o sync passa a usar essa conexão; se o token quebrar um dia, o estado mostra "erro" com caminho de reconexão (clicar de novo).
+- **Anti-goals de produto:** não é OAuth por pessoa (C114 continua valendo — UMA concessão, calendário da campanha); não é migração obrigatória (service account segue funcionando como fallback); não mexe na seleção de qual calendário (C150) nem no motor de reconciliação; leader não vê nem conecta (lockdown).
+
+### Esboço de fluxo (B)
+
+```text
+[/campanha/agenda — configuração do sync] → "Conectar com o Google" → consent Google (conta da campanha)
+→ callback de volta ao Teqo → estado "conectado" → sync usa a conexão (service account vira fallback)
+```
+
+### Rascunho UI (gate)
+
+- Rascunho UI (gate): `docs/plans/c149-conectar-google-oauth-ui-draft.html` — cenas: não configurado (botão), conectado, erro de token com reconexão, mobile.
+
+## Objetivo e aceite
+
+- O candidato/coordenador conecta a conta Google do calendário da campanha clicando um botão (consent do Google) — sem criar service account, sem colar JSON, sem tocar em GCP.
+- Estado da conexão visível e honesto: conectado | erro | não configurado; erro de token mostra caminho de reconexão em um clique.
+- Falha de token/Google não corrompe dados: Teqo continua SoT; o espelho degrada para "erro", nada é apagado nem duplicado.
+- Funciona atrás do túnel Cloudflare: o callback OAuth usa a URL pública correta (nada de localhost vazando no redirect).
+- Guardrails: o token nunca aparece em log; armazenamento fail-closed (sem conexão → sem sync, sem crash); desconectar revoga o acesso no Teqo e orienta revogar no Google; leader lockdown intocado.
+
+## Dados (intenção)
+
+- **Vou apresentar dados?** Não — a conexão é infraestrutura do espelho (saída); nenhuma leitura nova de dados no Teqo. O `calendarList` (escopo readonly) alimenta apenas a escolha de calendário do C150.
+- **Decisões desbloqueadas:** candidato/coordenador decide (uma vez) qual conta Google autoriza; diante de "erro", o mesmo ator decide reconectar ou desconectar.
+- **Forma:** _adiada ao plano de implementação_ — aqui só as restrições: estado ternário, nunca exibir o token, sem inventar estados intermediários.
+
+## Dados da decisão (literais)
+
+- Escopos OAuth mínimos: `https://www.googleapis.com/auth/calendar.events` + `https://www.googleapis.com/auth/calendar.calendarlist.readonly` (o segundo basta para listar calendários no C150 — sem escopo além do necessário).
+- Estados da conexão (UI + registro): `conectado | erro | não configurado` — exatamente esses três.
+- Env de credencial que vira fallback (não renomear nem remover nesta entrega): `GOOGLE_CALENDAR_SERVICE_ACCOUNT_KEY`.
+- Restrição dura de produto: o token DEVE sobreviver sem re-consentimento semanal (o caminho técnico — app unverified vs verificação Google — fica em aberto, ver Questões).
+
+## Direção no codebase (hipótese)
+
+- **Áreas prováveis:** configuração do sync na agenda `/campanha/agenda` (botão/estado), rotas de callback OAuth em `src/app/(campaign)/campanha/…`, utilitários Google perto de `googleCalendarSync.ts`/`googleCalendarClient.ts`, armazenamento da conexão na collection `googleCalendarSync` (onde `calendarId` já vive).
+- **Precedente a olhar:** plano C114 (decisão "não OAuth por pessoa"); par `readGoogleServiceAccountCredentials`/`googleCalendarClient` (REST hand-rolled com `jose`, sem lib Google hoje); WebAuthn de campaignUser (precedente de cerimônia de credencial com rota fora de `(app)`).
+- **Risco de acoplamento:** o token é credencial da conta Google da campanha — nunca logar, fail-closed, desconectar revoga; leader lockdown; C147 merged antes (o sync funcionando/diagnosticado) para não brigar no mesmo client.
+
+## Dependências
+
+- **Dura: C147** — o sync precisa estar funcionando/diagnosticado antes de trocar o modelo de credencial (também evita conflito de arquivos no client).
+
+## Fora de escopo
+
+- Escolha/listagem de qual calendário conectar → **C150** (aqui só entrega a conexão e o estado).
+- Migração/remoção obrigatória da service account (fica como fallback; aposentar é entrega futura).
+- OAuth por pessoa (anti-goal C114, reafirmado) e leitura de agenda pessoal para conflito de horário (item separado, se um dia existir).
+- Motor de reconciliação do sync (janela, locks, hooks C114/C122) — intocado.
+
+## Rabbit holes de produto
+
+- **"Verificação completa do app no Google."** Se alguém "só completar": auditoria, casework, semanas de fila — para uso interno. **Corte neste item:** caminho unverified (uso interno, <100 usuários) com aviso no consent; verificação só se o produto um dia precisar.
+- **"Desconectar + limpar tudo + migrar quem usa service account."** **Corte:** desconectar = revogar no Teqo + orientação de revogar no Google; ninguém é obrigado a migrar nesta entrega.
+- **"Seletor de calendário dentro do botão."** Se alguém "só completar": puxa o C150 para dentro. **Corte:** a conexão autoriza; escolher o calendário é o item seguinte.
+
+## Questões em aberto (produto)
+
+- **Manter a service account como fallback depois que o OAuth existir?** **Opções:** A) sim, como caminho técnico até o OAuth estar comprovado em produção | B) aposentar na mesma entrega. **Recomendação:** **A** — aposentar é entrega separada. _(assumido — validar com produto)_
+- **Quem pode conectar/desconectar?** **Opções:** A) candidato + coordenador | B) só candidato | C) também advisor. **Recomendação:** **A** — é a credencial da campanha inteira; advisor não autoriza credencial global, leader lockdown. _(assumido — validar)_
+- **App unverified ou verificação Google?** **Opções:** A) unverified (aviso no consent, token não expira em ~7 dias) | B) verificação completa (sem aviso, custo de auditoria). **Recomendação:** **A** — uso interno, <100 usuários; a restrição dura é o token sobreviver sem re-consentimento semanal. _(trade-off do aviso é a decisão de produto)_
+
+## Referências
+
+- Rascunho UI (gate): `docs/plans/c149-conectar-google-oauth-ui-draft.html`
+- Plan C114 (`c114-google-calendar-push-agenda-oficial.md` — decisão "não OAuth por pessoa"); C147 (dependência dura); C150 (seleção de calendário, fora daqui)
+- `src/utilities/googleCalendarSync.ts`, `src/utilities/googleCalendarClient.ts`, collection `googleCalendarSync` (`calendarId`)
+- `AGENTS-campaign.md` — leader lockdown / papéis de `/campanha`
diff --git a/docs/plans/c150-calendario-principal-e-inscricao-staff-ui-draft.html b/docs/plans/c150-calendario-principal-e-inscricao-staff-ui-draft.html
new file mode 100644
index 00000000..33c91a25
--- /dev/null
+++ b/docs/plans/c150-calendario-principal-e-inscricao-staff-ui-draft.html
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+ UI draft — C150 Calendário principal e inscrição de staff no Google
+
+
+
+
+ C150 — Agenda do Teqo: o núcleo (candidato/coordenador) escolhe o calendário principal da
+ campanha; o staff adiciona a agenda ao Google dele com um clique. Líder nunca vê estas
+ ações.
+
+
+
+
+ Cena 1 — desktop · seletor de calendário principal (candidato/coordenador)
+
+
+
+
+
Calendário principal da campanha
+
+ Criar e editar atividades espelham no calendário escolhido.
+
+
+
+
Calendários da conta Google conectada
+
+
+
+
+
+ Calendário da campanha
+ em uso
+
+
+
+ Meu calendário
+
+
+
+
+
+
+
+
+ Cancelar
+ Escolher calendário
+
+
+
+
+
+
+
+ Estado — desktop · sem conta Google conectada (depende de C149)
+
+
+
+
Calendário principal da campanha
+
+ Conecte a conta Google da campanha para listar e escolher o calendário principal.
+
+
+ Conectar conta Google
+
+
+
+
+
+
+
+ Cena 2 — desktop · chrome da agenda com o botão de staff
+
+
+
+
+
Agenda
+ Agosto de 2026
+ Google: sincronizado
+ Link de import
+ Adicionar ao meu Google Calendar
+
+
+
+
+ município
+
+
+
+ município
+
+
+
+
+ O recorte filtrado (município/tags) segue no fluxo atual do feed ICS: copiar link +
+ instrução "Do URL" — botão "Link de import".
+
+
+
+
+
+
+ Estado — desktop · confirmação da inscrição
+
+
+
+
+
Agenda da campanha adicionada ao seu Google Calendar.
+
+ Eventos e edições aparecem em minutos, conforme as notificações do seu Google.
+
+
+
+
+
+ município
+
+
+
+ município
+
+
+
+
+
+
+
+
+ Cena 3 — mobile 390px · botão de staff (caso de uso em campo)
+
+
+
+
+
+
Agenda
+ Google: sincronizado
+
+
+ Adicionar ao meu Google Calendar
+ Link de import
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/plans/c150-calendario-principal-e-inscricao-staff.md b/docs/plans/c150-calendario-principal-e-inscricao-staff.md
new file mode 100644
index 00000000..c41a0025
--- /dev/null
+++ b/docs/plans/c150-calendario-principal-e-inscricao-staff.md
@@ -0,0 +1,100 @@
+# C150 — Agenda: calendário principal escolhido pelo candidato/coordenador + inscrição de staff no Google
+
+Status: rascunho
+Atualizado em: 2026-08-27
+Issue: #935
+Priority: P2
+Model: composer-2.5
+Depends: #934 (C149)
+Impeccable: B — encaixe na agenda
+Rascunho UI: docs/plans/c150-calendario-principal-e-inscricao-staff-ui-draft.html
+Appetite: ~1,5–2 dias eng; um outcome verificável — o candidato/coordenador escolhe o calendário principal da campanha na própria agenda (create/edit espelham nele) e um staff adiciona a agenda ao Google dele com um clique
+Responsável: —
+
+## Intenção
+
+Dois pedidos fundidos sobre o espelho Google Calendar entregue por C114/C115. Hoje, escolher QUAL calendário Google espelha a agenda exige o admin do Payload — mas quem decide o calendário oficial da campanha é o candidato/coordenador. E inscrever a agenda no Google pessoal de cada staff ainda é manual: copiar o link e configurar "Por URL" à mão — o próprio pedido reconhece que o caminho certo é um botão que leva ao Google já preparado. Na agenda do Teqo: o núcleo escolhe o calendário principal entre os da conta Google conectada (C149) e o staff adiciona a agenda ao Google dele com um clique.
+
+## Persona e fluxo
+
+- **Persona / contexto:** candidato/coordenador na mesa decidindo a agenda oficial; assessores e comunicação em campo, vivendo no Google Calendar do celular; líder de campo nunca toca nisso.
+- **Job principal:** "escolho qual calendário da nossa conta Google é a agenda oficial — e a agenda da campanha aparece no meu Google Calendar sem eu configurar nada à mão".
+- **Fluxo desejado:**
+ 1. Na agenda (`/campanha/agenda`), o candidato/coordenador abre a configuração do Google e vê os calendários da conta conectada.
+ 2. Escolhe um como calendário principal e confirma; criar/editar atividades passam a espelhar nele.
+ 3. Um staff (coordenador/assessor/candidato) clica "Adicionar ao meu Google Calendar": o Google abre com o calendário oficial pré-configurado — sem copiar link nem "Por URL".
+ 4. Para recorte filtrado (município/tags), o caminho continua o feed ICS atual: copiar link + instrução — nada muda de comportamento.
+- **Anti-goals de produto:** não escrever no calendário PESSOAL de cada staff (decisão C114); não criar N calendários espelhados — o calendário principal é UM; não refazer o feed ICS nem o motor de sync; sem import de eventos do Google além do que C115 já faz; leader lockdown intocado.
+
+### Esboço de fluxo (B)
+
+```text
+[agenda /campanha/agenda] → "Calendário principal" (candidato/coordenador)
+→ lista os calendários da conta Google conectada (C149) → escolhe → confirma
+→ espelho re-concilia no calendário escolhido; create/edit espelham nele
+
+[mesma agenda, staff] → "Adicionar ao meu Google Calendar" → um clique
+→ Google abre com o calendário oficial pré-configurado → agenda no Google pessoal
+```
+
+### Rascunho UI (B)
+
+- Rascunho UI (gate): `docs/plans/c150-calendario-principal-e-inscricao-staff-ui-draft.html`
+
+## Objetivo e aceite
+
+- Na própria agenda, o candidato/coordenador lista os calendários da conta Google conectada e escolhe o calendário principal — configurar o espelho deixa de exigir o admin do Payload.
+- Criar/editar atividades espelham no calendário escolhido — o motor C114/C115 não muda de modelo; trocar o calendário re-concilia o espelho sem eventos órfãos/duplicados visíveis ao staff.
+- Um staff adiciona a agenda ao Google pessoal com um clique — sem copiar link nem configurar "Por URL" à mão; o recorte filtrado segue no fluxo atual do feed ICS (link + instrução).
+- Guardrails: leader não vê nem usa nenhuma das duas ações; Teqo continua SoT; o calendário principal é UM; falha do Google não derruba a agenda do Teqo (estado do sync continua visível); o admin do Payload continua como escape hatch da configuração.
+
+## Dados (intenção)
+
+- **Vou apresentar dados?** Não — o espelho é saída (mesma posição do C114); nenhuma leitura nova no Teqo.
+- **Decisões desbloqueadas:** candidato/coordenador escolhe o calendário principal da campanha (ator + escolha, hoje exclusiva do admin do Payload); staff opta por inscrever a agenda no Google pessoal dele (ator + escolha, hoje manual).
+- **Forma:** _adiada ao plano de implementação_ — a apresentação do evento no Google já é decisão do C114.
+
+## Dados da decisão (literais)
+
+- Padrão do link one-click do calendário oficial: `https://calendar.google.com/calendar/r?cid=` — confiável para calendar IDs de calendários Google reais (caso do calendário oficial da campanha). O builder atual compõe o `cid` com a URL webcal pública do calendário — reconciliar com este literal na implementação.
+- Para o recorte ICS, quando um one-click for tentado: `cid` com `webcal://` — INFIEL/instável para feeds ICS externos ("Unable to add calendar", eventos não populam); fallback obrigatório = copiar link + instrução "Do URL" (fluxo atual). Assumido v1: NÃO tentar — só link + instrução.
+- Rótulos de placeholder do rascunho UI: "Calendário da campanha" (oficial) e "Meu calendário" (pessoal genérico da conta conectada).
+
+## Direção no codebase (hipótese)
+
+- **Áreas prováveis:** chrome da agenda em `src/components/campaign/activity/` (`AgendaGoogleSyncChrome.tsx`, `GoogleCalendarSyncDialog.tsx`, `CalendarFeedDialog.tsx`), ações em `src/app/(campaign)/campanha/actions/googleCalendarSync.ts`, papel em `src/utilities/access/googleCalendarSync.ts` (hoje `canSetGoogleCalendarSyncConfigField = isPayloadAdmin` — é isso que o produto quer abrir ao candidato/coordenador), links em `src/lib/googleCalendarLink.ts`.
+- **Precedente a olhar:** plans C114/C115 (espelho e bidirecional — motor não muda de modelo), C16/C113 (feed ICS); trocar `calendarId` já dispara re-conciliação automática (config hook D7; snapshot pino por calendarId; canal push re-criado por calendário) — o delta é a troca de dono da escolha, não a mecânica.
+- **Risco de acoplamento:** abrir a escolha a candidato/coordenador muda uma permissão de configuração hoje admin-only (a intenção do produto é exatamente essa); leader lockdown intocado; o admin do Payload permanece funcionando.
+
+## Dependências
+
+- **Dura:** C149 — o seletor nasce da conexão OAuth (listar os calendários da conta conectada).
+- Suaves: C114/C115 (motor do espelho, em produção), C16/C92/C98/C113 (feed ICS e link de import).
+
+## Fora de escopo
+
+- Escrever no calendário pessoal de cada staff via OAuth individual → anti-goal reafirmado da decisão C114.
+- N calendários espelhados (um por filtro/papel) → recorte continua no feed ICS (C16/C113).
+- Refazer o feed ICS, o motor de sync ou o import bidirecional do C115.
+- One-click universal para feeds ICS externos → infiável (pesquisa); o recorte mantém link + instrução.
+- Escolher calendário principal por assessor → decisão de núcleo; assessor só inscreve.
+
+## Rabbit holes de produto
+
+- **"Um calendário principal por filtro/papel."** Se alguém "só completar": N espelhos divergentes e permissões por calendário. **Corte neste item:** um calendário principal, escolhido pelo núcleo; recorte continua no feed ICS.
+- **"One-click para todo tipo de agenda (incl. ICS)."** Se alguém "só completar": botão que falha com "Unable to add calendar" e gera suporte. **Corte:** one-click só para o calendário oficial (Google real); recorte mantém copiar link + instrução.
+- **"Sincronizar todo o histórico ao trocar de calendário."** **Corte:** o motor atual governa a janela; a troca re-usa a re-conciliação existente, sem backfill novo.
+
+## Questões em aberto (produto)
+
+- **O botão one-click do recorte filtrado (ICS) vale em v1?** **Opções:** A) sim, como tentativa com fallback claro | B) não — só o calendário oficial em v1, recorte segue link+instrução. **Recomendação:** B — o padrão cid é infiel para ICS e a falha custa confiança. _(assumido — validar no gate)_
+- **A escolha do calendário principal substitui completamente a config no admin do Payload?** **Opções:** A) substitui na UI da agenda; admin fica como escape hatch | B) mantém as duas como superfícies de primeira classe. **Recomendação:** A — uma só casa para a decisão; admin permanece para operação/contingência. _(assumido — validar)_
+- **Assessor pode escolher o calendário principal?** **Opções:** A) só candidato/coordenador | B) também assessor. **Recomendação:** A — decisão de núcleo sobre a agenda oficial; assessor apenas inscreve a agenda no Google dele.
+
+## Referências
+
+- GitHub Issue: — (após `pnpm agent:register`)
+- Rascunho UI (gate): `docs/plans/c150-calendario-principal-e-inscricao-staff-ui-draft.html`
+- Plans C114 (`c114-google-calendar-push-agenda-oficial.md`), C115 (`c115-google-calendar-edicao-bidirecional.md`), C16 (`sync-teqo-google-calendar.md`), C113 (`c113-feed-ical-agenda-nao-atualiza.md`)
+- Arquivos para abrir primeiro: `src/lib/googleCalendarLink.ts`, `src/components/campaign/activity/AgendaGoogleSyncChrome.tsx`, `src/components/campaign/activity/GoogleCalendarSyncDialog.tsx`, `src/components/campaign/activity/CalendarFeedDialog.tsx`, `src/utilities/access/googleCalendarSync.ts`, `src/collections/GoogleCalendarSync.ts`, `src/utilities/activityUi.ts`
+- `AGENTS-campaign.md` — convenção de papéis/lockdown tocada pelo item
diff --git a/docs/plans/c151-atividade-evento-publico-ui-draft.html b/docs/plans/c151-atividade-evento-publico-ui-draft.html
new file mode 100644
index 00000000..55d4c4d7
--- /dev/null
+++ b/docs/plans/c151-atividade-evento-publico-ui-draft.html
@@ -0,0 +1,274 @@
+
+
+
+
+
+
+ UI draft — Atividade: marcar evento como público (C151)
+
+
+
+
+ Staff edita uma atividade no modal e marca "Evento público" (default desmarcado); a marcação
+ é durável — nenhuma tela muda de comportamento.
+
+
+ Cinza = contexto existente (sem mudança) · acento = novo
+ neste item. Posição do checkbox segue a recomendação (junto dos outros checkboxes) — validar
+ no gate.
+
+
+
+
+ Cena 1 · desktop — modal de atividade, seções "Informações básicas" e "Agendamento"
+
+
+
+
+
Editar atividade
+
/campanha/atividades
+
+ ×
+
+
+
+ Informações básicas
+
+
+
+
Título
+
+
+
+
+
+
+
Tipo
+
+
+ ▾
+
+
+
+
Status
+
+
+ ▾
+
+
+
+
+
+
+
+ Agendamento
+
+
+
+
+
Início
+
+
+
+
+
+
Fim
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancelar
+ Salvar
+
+
+
+
+
+
+ Cena 2 · mobile (~390px) — mesma cena
+
+
+
+
+
Editar atividade
+
/campanha/atividades
+
+ ×
+
+
+
+ Informações básicas
+
+
+
+
Título
+
+
+
+
+
+
+
Tipo
+
+
+ ▾
+
+
+
+
Status
+
+
+ ▾
+
+
+
+
+
+
+
+ Agendamento
+
+
+
+
+
Início
+
+
+
+
+
+
Fim
+
+
+
+
+
+
+
+
+
+
+
+
+ Cancelar
+ Salvar
+
+
+
+
+
+ Rascunho descartável (gate) — registro do aceite visual. Zero JS além do build browser do
+ Tailwind v4; sem comportamento.
+
+
+
+
diff --git a/docs/plans/c151-atividade-evento-publico.md b/docs/plans/c151-atividade-evento-publico.md
new file mode 100644
index 00000000..e91c1862
--- /dev/null
+++ b/docs/plans/c151-atividade-evento-publico.md
@@ -0,0 +1,94 @@
+# Atividade: marcar evento como público (base para exposição no site)
+
+Status: rascunho
+Atualizado em: 2026-08-27
+Issue: #936
+Priority: P3
+Model: composer-2.5
+Serializes: #933 (C148)
+Impeccable: B — encaixe no modal de atividade
+Rascunho UI: docs/plans/c151-atividade-evento-publico-ui-draft.html
+Appetite: ~0,5 dia eng; um outcome verificável — o staff marca/desmarca "Evento público" e a marcação é durável e consultável, sem mudar nada no comportamento atual das telas
+Responsável: —
+
+## Intenção
+
+"Let's add a new property to activities to register it as a public event. Public events will later be exposed in the candidate's website to be shareable for followers/supporters." — hoje uma atividade (caminhada, comício, agenda do mandato) é só registro interno: quando o time quiser divulgar um evento no site, não há como distinguir o que é público do que é operação interna. Este item adiciona essa distinção — uma marcação simples, durável e consultável — para que a futura agenda pública do site tenha um dado confiável de onde partir. A exposição em si é outro item (PUB): aqui só se marca a intenção.
+
+## Persona e fluxo
+
+- **Persona / contexto:** staff de campanha (coordenador/assessor/candidato) editando atividades em `/campanha/atividades`, no escritório ou em campo; liderança não entra nesta história.
+- **Job principal:** ao cadastrar/editar uma atividade, marcar se ela é um evento público — sem planilha paralela nem precisar lembrar disso depois.
+- **Fluxo desejado:** abrir o modal da atividade → marcar/desmarcar "Evento público" (desmarcado por padrão) → salvar; a marcação persiste e volta correta ao reabrir. Nenhuma tela muda de comportamento.
+- **Anti-goals de produto:** página pública ou share kit agora; segundo cadastro de "evento" paralelo à atividade; filtro/badge nova na lista; flag alterando o espelho do Google ou o access atual.
+
+### Esboço de fluxo (B/C/D)
+
+```text
+[início: modal de atividade] → staff marca "Evento público" (default: desmarcado)
+→ salvar → [outcome: marcação durável e consultável; nenhuma tela muda de comportamento]
+```
+
+### Rascunho UI (B/C/D)
+
+- Rascunho UI (gate): `docs/plans/c151-atividade-evento-publico-ui-draft.html` — cenas desktop + mobile (~390px) do modal com o novo checkbox ao lado dos existentes.
+
+## Objetivo e aceite
+
+- O staff que edita atividades marca/desmarca "Evento público"; a marcação persiste entre sessões, volta correta ao reabrir a atividade e fica consultável para o consumidor futuro — sem nenhum comportamento novo visível nas telas.
+- **Guardrails de produto:** a flag marca INTENÇÃO de exposição; a exposição é FUTURA (item PUB posterior). Nada no site público muda; nada na agenda muda de comportamento.
+- **Guardrail para o FUTURO consumidor:** atividade pública nunca expõe lideranças, tarefas, resultados nem dados operacionais internos — a superfície pública futura mostra o "cartaz do evento" (título, data/hora, município/local, descrição), nunca o backend operacional.
+- Quem marca é o mesmo staff que edita atividades (coordenador/assessor/candidato); liderança segue em lockdown e não vê o campo nem nada novo.
+
+## Dados (intenção)
+
+- **Vou apresentar dados?** Não — nenhuma superfície de dados neste item; a flag é insumo durável para o item PUB futuro (agenda pública, PUB3 #45, blocked).
+- **Decisões desbloqueadas:** staff + escolha de quais atividades são candidatas a exposição pública; o item PUB futuro decide o quê/como expor.
+- **Forma:** _adiada ao plano de implementação_ — aqui só a restrição de produto acima (cartaz do evento, nunca backend operacional).
+
+## Dados da decisão (literais)
+
+- Rótulo de UI (checkbox): "Evento público" — default desmarcado.
+- Descrição admin pt-BR (curta): "Quando marcado, este evento poderá ser publicado no site do candidato (futuro)."
+- Quem pode marcar: os mesmos papéis que editam atividades hoje (coordinator/advisor/candidate); liderança não vê o campo.
+
+## Direção no codebase (hipótese)
+
+- **Áreas prováveis:** collection `activity` (`src/collections/Activity.ts`), modal central `src/components/campaign/activity/` (ActivityOverlay — create+edit), detalhe `/campanha/atividades/[slug]`; Payload admin herda.
+- **Precedente a olhar:** checkboxes no mesmo arquivo — `deputyPresent` (~L493) e `allDay` (~L508), ambos checkbox com default desmarcado, indexado e descrição admin; migrations recentes de campo simples (`20260819_213947_add_site_settings_facebook_pixel_id`, `20260823_161905_add_municipality_update_deliberation`) + regeneração de tipos.
+- **Risco de acoplamento:** `SYNC_RELEVANT_ACTIVITY_FIELDS` (`googleCalendarSync.ts:100`) decide se campo dispara re-sync — recomendação v1: NÃO incluir (calendário oficial segue full-staff). Access atual (`canReadActivity` inclui leader) NÃO muda; flag de negócio nova não pede field-access especial.
+
+## Dependências
+
+- **Serializa com C148** (mesmo modal de atividade — não trabalhar em paralelo).
+- PUB3 (#45) — consumidor futuro da flag (agenda pública); está blocked e NÃO é desbloqueado por este item (aqui não se constrói exposição).
+- C114 (espelho do Google) — interação a respeitar: decisão v1 = flag não influencia o sync.
+
+## Fora de escopo
+
+- Página/agenda pública no site e share kit — item PUB futuro (PUB3 #45); nada no `(frontend)` muda.
+- Qualquer consumo/exposição da flag agora (site, feed, filtro, badge na lista).
+- Collection nova ou modelagem paralela de "evento público" — a marcação vive na atividade existente.
+- Mudança de access/visibilidade (liderança continua sem ver nada novo; Payload admin segue como está).
+- Integração da flag com o espelho do Google (decisão v1: não influencia).
+- Marcação em lote de atividades existentes (spreadsheet mode — sem pedido).
+
+## Rabbit holes de produto
+
+- **"Já deixar a página pública pronta."** Se alguém "só completar": agenda pública, SEO, share kit — explosão de escopo. **Corte neste item:** só a marcação; exposição é item PUB futuro.
+- **"Aproveitar e ligar a flag ao espelho do Google."** Se alguém "só completar": re-escopo do calendário oficial (full-staff) e conflito com C114. **Corte neste item:** sync intocado em v1.
+- **"Criar collection 'evento público' separada."** Se alguém "só completar": segundo cadastro de atividade, dados divergentes. **Corte neste item:** flag na atividade existente.
+- **"Filtrar/badgear a lista por eventos públicos."** Se alguém "só completar": redesign de tabs/filtros da agenda. **Corte neste item:** nenhuma tela muda de comportamento.
+
+## Questões em aberto (produto)
+
+- **Em qual seção do modal entra o checkbox?** **Opções:** A) junto dos outros checkboxes do modal (seção básica/agendamento) | B) seção própria "Publicação" | C) campo avançado/oculto. **Recomendação:** A — um checkbox a mais não pede seção nova; o rascunho o mostra ao lado dos existentes. _(assumido — validar no gate com o rascunho)_
+- **A flag influencia o espelho do Google?** **Opções:** A) NÃO em v1 — calendário oficial continua full-staff; a flag existe para o site futuro | B) sim, re-scope do sync. **Recomendação:** A — mudar o sync agora complica C114 sem ganho de produto hoje. _(recomendação — validar com produto)_
+
+## Referências
+
+- GitHub Issue: — (após `pnpm agent:register`)
+- Rascunho UI (gate): `docs/plans/c151-atividade-evento-publico-ui-draft.html`
+- `src/collections/Activity.ts` (precedentes `deputyPresent`/`allDay`) · `src/components/campaign/activity/ActivityOverlay.tsx`
+- `src/utilities/access/activities.ts` · `src/utilities/googleCalendarSync.ts:100` · `src/collections/Post.ts:78` (precedente público mais próximo: `Post.type` valor `evento`)
+- `AGENTS-campaign.md` — seção "Campaign activities (C3)" · PUB3 (#45)