feat(B179): Sollinha persona no gênero masculino (prompt + placeholder + docs)
This commit is contained in:
parent
bbe93da4ad
commit
6760b99942
7 changed files with 74 additions and 7 deletions
|
|
@ -4,6 +4,8 @@ Conteúdo movido do `AGENTS.md` em 2026-07-30 (fatiamento do paradigma de agente
|
|||
|
||||
---
|
||||
|
||||
**Recently resolved (2026-08-09):** **B179** (Issue #462) — o Sollinha passou a falar de si **no masculino**: o system prompt (`src/utilities/ai/systemPrompt.ts`) abre com "Você é **o** Sollinha" e ganhou regra explícita (auto-referência masculina em primeira pessoa, nunca flexões femininas sobre si) — o welcome da UI já era masculino; o placeholder do chat virou "Pergunte para o Sollinha..." (`CampaignAIChat.tsx`) com os seletores e2e acompanhando (2 specs). Sem migration, access, Consent ou guarda automatizada (decisão do gate: só édito de texto). Os planos entregues da família (B162/B166/B167/B173/B174/B177) permanecem frozen com "a Sollinha" histórico — o que vale é a voz no runtime e o que se escreve daqui pra frente. Intenção [`sollinha-genero-masculino.md`](plans/sollinha-genero-masculino.md); impl [`sollinha-genero-masculino-impl.md`](plans/sollinha-genero-masculino-impl.md).
|
||||
|
||||
**Recently resolved (2026-08-09):** **B181** (Issue #480) — a `/campanha/agenda` não exibe mais o bloco "Nenhum compromisso nesta janela e neste filtro" + botão "Criar atividade" acima do calendário em janela vazia (só ocupava espaço, redundante com slot-click C91, botão "Nova atividade" dos filtros e FAB C94). Remoção cirúrgica em `ActivityAgenda.tsx` (bloco condicional + órfãos `emptyHref`/imports `Link` e `buildActivityCreateHref`); `buildActivityCreateHref` permanece com 2 consumidores ("Mais detalhes" no inline create, "Nova atividade" nos filtros); loading/erro e `noEventsText` do FullCalendar (vista "Lista") intactos. Teste unitário de regressão: janela vazia não renderiza aviso nem link de criação. Sem migration, access, Consent ou schema. Intenção [`agenda-remover-aviso-vazio.md`](plans/agenda-remover-aviso-vazio.md); impl [`agenda-remover-aviso-vazio-impl.md`](plans/agenda-remover-aviso-vazio-impl.md).
|
||||
|
||||
**Recently resolved (2026-08-09):** **C93** (Issue #437) — botão "Link de import" da agenda destravado **sem filtros** (sucessor direto do C16): com zero filtros ativos, staff gera o link do feed **completo dentro do escopo de leitura do criador** — coordinator/candidate = tudo; advisor = portfólio dos municípios que administra — com fail-closed/revogação de C16 intactos. O servidor já cobria o caso (vazio = escopo do criador em `buildFeedWhere`/`resolveFeedCreatorAccess`; descrição do campo `filterMunicipality`); a entrega removeu o gate `hasFilters → disabled` de `CalendarFeedButton` (prop morto `state` incluído), manteve `hasFilters` só no "Limpar filtros" e neutralizou a copy do diálogo ("este recorte" → "a agenda"). Testes: 3 int de conteúdo sem filtro (coordinator = A+B; advisor = só o portfólio, nada fora vaza; advisor sem município → feed vazio fail-closed) + unit component (botão habilitado com zero filtros e fluxo nomear→criar). Sem migration, access, Consent ou schema. Intenção [`c93-gerar-link-import-sem-filtros.md`](plans/c93-gerar-link-import-sem-filtros.md); impl [`c93-gerar-link-import-sem-filtros-impl.md`](plans/c93-gerar-link-import-sem-filtros-impl.md).
|
||||
|
|
|
|||
64
docs/plans/sollinha-genero-masculino-impl.md
Normal file
64
docs/plans/sollinha-genero-masculino-impl.md
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Impl: Sollinha: persona no gênero masculino (prompt + referências .md)
|
||||
|
||||
Status: aprovado
|
||||
Atualizado em: 2026-08-09
|
||||
Issue: #462
|
||||
Intenção: docs/plans/sollinha-genero-masculino.md
|
||||
Appetite restante: ~0,25 dia (édito de texto; herdado)
|
||||
|
||||
## Leitura da intenção
|
||||
|
||||
- **Outcome:** o Sollinha se apresenta e se refere a si **no masculino** ("sou o Sollinha", "estou à disposição"), sem renomear o produto nem mudar tom/formalidade.
|
||||
- **O que NÃO negociar:** sem guarda automatizada (só édito de texto); não reescrever planos entregues/frozen; nome "Sollinha" permanece; nenhuma mudança em tools/RBAC/rate limit/fluxo.
|
||||
- **O que reavaliar:** a hipótese de "referências .md editáveis" da intenção (ex.: `sollinha-tool-urls-navegacao.md`, `sollinha-cidades-mais-votado.md`) está **desatualizada**: B162 (#383) e B177 (#460) já estão `done/in-prod` → planos entregues → **frozen**. O surface de docs editáveis é efetivamente vazio — a entrega é runtime-first.
|
||||
|
||||
## Abordagem recomendada
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[systemPrompt.ts<br/>linha 1 + regra explícita] --> B[Voz masculina no runtime]
|
||||
C[CampaignAIChat.tsx<br/>placeholder opcional] --> D[Consistência UI]
|
||||
E[CHANGELOG + plano próprio] --> F[Registro da entrega]
|
||||
```
|
||||
|
||||
**Opções consideradas:** A (só prompt) | B (prompt + placeholder UI) | C (prompt + placeholder + docs não-frozen)
|
||||
**Recomendação:** **B** — o prompt é o núcleo (aceite); o placeholder `Pergunte para a Sollinha...` usa artigo feminino no texto visível ao usuário e é a única outra superfície runtime de gênero; custo zero e consistente com o welcome já masculino ("Olá! Eu sou o Sollinha").
|
||||
**Rejeitadas:**
|
||||
|
||||
- **C** — verificado no repo: os únicos docs da família com "a Sollinha" são **entregues/frozen** (B162, B166, B167, B173, B174, B177 + seus `-impl.md`) ou de Issue **in-progress** de outro agente (`salvador-pagina-detalhe-cidade.md`, B178 — proibido tocar). O único plano não-frozen da família (`sollinha-tools-eleitorais-leader-lockdown.md`, B180) já usa "no Sollinha" (masculino). Regra do repo: entregue = imutável; nada a editar.
|
||||
- **Guarda automatizada / teste de convenção** — decisão explícita do gate da intenção ("sem guarda automatizada").
|
||||
- **Renomear produto / mudar tom** — anti-goal da intenção.
|
||||
|
||||
### Componentes / mudanças
|
||||
|
||||
- **`AI_SYSTEM_PROMPT`** (`src/utilities/ai/systemPrompt.ts`): linha 1 `Você é a Sollinha` → `Você é o Sollinha`; adicionar regra explícita na seção "Quem você é" (flexões masculinas de primeira pessoa; nunca adjetivos/particípio no feminino sobre si — ex.: "obrigado", não "obrigada").
|
||||
- **`CampaignAIChat.tsx`** (placeholder): `Pergunte para a Sollinha...` → `Pergunte para o Sollinha...` (único caso com artigo feminino na UI; welcome já é masculino).
|
||||
- **E2E existentes** (manutenção de seletor, não guarda nova): 3 referências ao placeholder nos specs `campaignAiChatResize.e2e.spec.ts:23` e `campaignAiTranscribe.e2e.spec.ts:84,128` acompanham o texto.
|
||||
- **Docs:** sem edição de planos entregues (frozen). Edita-se apenas o plano desta entrega (`sollinha-genero-masculino.md` — status no fim, quote da referência corrigida pós-fix) e a entrada curta em `docs/CHANGELOG-AGENTS.md` (redigida já no masculino).
|
||||
- **Migration:** sem migration. **Access / Consent:** nenhum (sem schema, sem RBAC, sem PII). **UI:** Impeccable A — N/A (só string literal).
|
||||
|
||||
## Fases verificáveis
|
||||
|
||||
1. **Runtime** — editar `systemPrompt.ts` (linha 1 + regra). (quota: 0,1 dia)
|
||||
2. **UI + specs** — placeholder `CampaignAIChat.tsx` + 3 seletores e2e. (quota: 0,05 dia)
|
||||
3. **Docs/registro** — entrada `docs/CHANGELOG-AGENTS.md` + atualizar plano da entrega. (quota: 0,05 dia)
|
||||
4. **Gates** — `pnpm gate:fast`; e2e dos 2 specs tocados; `pnpm push` entrega.
|
||||
|
||||
## Rabbit holes / Não escopo (engenharia)
|
||||
|
||||
- Caçar "a Sollinha"/"da Sollinha" em planos entregues ou no `CHANGELOG-AGENTS.md` (histórico congelado — fica como referência de época).
|
||||
- Editar `salvador-pagina-detalhe-cidade.md` (B178, in-progress de outro agente) — mesmo tendo "da Sollinha".
|
||||
- Teste de convenção / guarda automatizada de gênero (decisão do gate).
|
||||
- Ajustar modelo/empresa de LLM (voz vem do prompt; reavaliação só com exemplo gravado, registro no plano sem projeto).
|
||||
|
||||
## Riscos e mitigação
|
||||
|
||||
- **Deriva do modelo apesar do prompt** (regra explícita não elimina 100%): risco conhecido e aceito pela intenção — reavaliar com exemplo gravado se recorrer; sem projeto agora.
|
||||
- **E2E quebrado pelo seletor novo**: mitigado — os specs tocam o placeholder exatamente 3 vezes; rodar os 2 arquivos antes do push.
|
||||
- **Divergência da hipótese da intenção (docs "editáveis")**: mitigado por este plano — o aceite (prompt define "o Sollinha" + regra explícita) permanece integralmente coberto; o surface .md não exige edição.
|
||||
|
||||
## Aceite de engenharia
|
||||
|
||||
- [x] Aceite de produto da intenção ainda coberto (prompt define "Você é o Sollinha" + regra explícita; docs novos/no futuro usam "o Sollinha")
|
||||
- [x] Invariantes AGENTS/engineering-standards (sem migration, sem access, sem Consent; nada de histórico frozen tocado)
|
||||
- [x] Sem testes de domínio novos — decisão de produto: édito de texto sem guarda automatizada; só manutenção de seletores e2e existentes
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# B179 — Sollinha: persona no gênero masculino (prompt + referências .md)
|
||||
|
||||
Status: plano — registrado (blocked até plano em main)
|
||||
Status: entregue (2026-08-09 — prompt + UI no masculino)
|
||||
Atualizado em: 2026-08-09
|
||||
Issue: #462
|
||||
Priority: P2
|
||||
|
|
@ -70,6 +70,6 @@ Resolvidas no gate (2026-08-09):
|
|||
## Referências
|
||||
|
||||
- Canvas UI (gate): N/A
|
||||
- `src/utilities/ai/systemPrompt.ts` (linha 1: “Você é a Sollinha…”)
|
||||
- `src/utilities/ai/systemPrompt.ts` (linha 1: “Você é o Sollinha…” — corrigido no B179)
|
||||
- Planos da família: [`ai-chat-sollinha.md`](ai-chat-sollinha.md) (frozen), [`sollinha-tool-urls-navegacao.md`](sollinha-tool-urls-navegacao.md) (ready/editable), [`sollinha-cidades-mais-votado.md`](sollinha-cidades-mais-votado.md) (novo)
|
||||
- AGENTS.md — convenções de naming/idioma (rótulos visíveis em pt, identificadores em inglês); regra de planos frozen
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ export const CampaignAIChat = ({ className }: { className?: string }) => {
|
|||
handleSubmit(e)
|
||||
}
|
||||
}}
|
||||
placeholder={isRecording ? 'Ouvindo...' : 'Pergunte para a Sollinha...'}
|
||||
placeholder={isRecording ? 'Ouvindo...' : 'Pergunte para o Sollinha...'}
|
||||
disabled={busy}
|
||||
rows={1}
|
||||
className="min-h-0 flex-1 resize-none border-0 bg-transparent px-3 py-2 text-sm outline-none placeholder:text-muted-foreground disabled:opacity-50"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
export const AI_SYSTEM_PROMPT = `Você é a Sollinha, assistente virtual da campanha de Jorge Solla (PT-BA) para deputado federal.
|
||||
export const AI_SYSTEM_PROMPT = `Você é o Sollinha, assistente virtual da campanha de Jorge Solla (PT-BA) para deputado federal.
|
||||
|
||||
## Quem você é
|
||||
- Você é prestativo, direto e conhece profundamente os dados da campanha.
|
||||
- Você se refere a si mesmo sempre no masculino, em primeira pessoa: "sou o Sollinha", "estou à disposição", "obrigado". Nunca flexione no feminino adjetivos ou particípios sobre você (ex.: "sou a Sollinha", "obrigada", "estou disponível" — não "disponível" no feminino).
|
||||
- Você fala português do Brasil, em tom profissional mas acolhedor.
|
||||
- Você NUNCA inventa dados. Se uma ferramenta retornar "não encontrado", você diz isso claramente.
|
||||
- Quando uma ferramenta retorna dados numéricos, você os apresenta de forma clara, com contexto.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const MESSAGE = 'Mensagem que migra entre as superficies'
|
|||
|
||||
const openChatAndSend = async (page: Page) => {
|
||||
await expect(page.getByText('Olá! Eu sou o Sollinha')).toBeVisible({ timeout: 20_000 })
|
||||
const input = page.getByRole('textbox', { name: 'Pergunte para a Sollinha...' })
|
||||
const input = page.getByRole('textbox', { name: 'Pergunte para o Sollinha...' })
|
||||
await input.fill(MESSAGE)
|
||||
await input.press('Enter')
|
||||
await expect(page.getByText(MESSAGE)).toBeVisible({ timeout: 20_000 })
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ test.describe('B173 — consulta por voz no chat da Sollinha', () => {
|
|||
await mockAiChat(page)
|
||||
await openChat(page)
|
||||
|
||||
const input = page.getByRole('textbox', { name: 'Pergunte para a Sollinha...' })
|
||||
const input = page.getByRole('textbox', { name: 'Pergunte para o Sollinha...' })
|
||||
|
||||
// Tap the mic → the bar enters "Ouvindo..." while we "speak".
|
||||
await page.getByRole('button', { name: 'Falar pergunta (voz)' }).click()
|
||||
|
|
@ -125,7 +125,7 @@ test.describe('B173 — consulta por voz no chat da Sollinha', () => {
|
|||
})
|
||||
|
||||
// Text chat still works.
|
||||
const input = page.getByRole('textbox', { name: 'Pergunte para a Sollinha...' })
|
||||
const input = page.getByRole('textbox', { name: 'Pergunte para o Sollinha...' })
|
||||
await input.fill('Foco no texto')
|
||||
await input.press('Enter')
|
||||
await expect(page.getByText('Foco no texto')).toBeVisible({ timeout: 20_000 })
|
||||
|
|
|
|||
Loading…
Reference in a new issue