Files
portifolio-evandrodevbr/docs/superpowers/specs/2026-09-04-seo-geo-growth-design.md

392 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design: crescimento SEO e GEO do portfólio
**Status:** pronto para execução somente após o hardening técnico
**Data:** 2026-09-04
**Pesquisa-base:** `.superpowers/seo-research-2026-09-04.md`
**Plano predecessor:** `docs/superpowers/plans/2026-09-04-portfolio-hardening.md`
## Objetivo
Melhorar descoberta, compreensão e confiança do portfólio em busca orgânica e experiências generativas sem redesenhar a interface, criar páginas por cidade ou publicar alegações sem prova. A implementação corrige frescor e entidades, torna metadados e Open Graph específicos por rota, conecta serviços a evidências, aprofunda três cases sustentados pelo próprio repositório e por código público, separa os caminhos de contratação e consultoria e documenta medição/operações externas sem alegar que foram executadas.
## Gate de sequência
SEO começa **depois** do gate final do hardening. Na data deste design, apenas a Task 1 do hardening estava concluída; as Tasks 2–12 ainda alterariam arquivos compartilhados. O executor deve aceitar como baseline o estado final comprovado pelo predecessor, não restaurar o estado observado durante esta pesquisa.
Interfaces finais que este design consome:
- `PortfolioProjectView` preserva todo projeto editorial e anexa `demoAvailability`/`demoCheckedAt`.
- `app/layout.tsx` é server-first, sem providers globais de scroll/animação, e mantém Header, overlays leves, conteúdo e analytics.
- `GoogleAnalytics` usa consentimento opt-in, chave `evandro.analytics-consent.v1`, valores `granted | denied`, endpoint oficial do Google e revogação alcançável.
- `package.json` oferece `pnpm lint`, `pnpm typecheck`, `pnpm test`, `pnpm build`; Node 22, pnpm 9.15.9 e Next 16.3.0 ficam fixados pelo ruling de segurança do hardening.
- `Footer`, `Contact`, hero command-center, globo, starfield, notebook, tipografia, paleta preto/papel/verde ácido, cartões e tokens `ds-*` permanecem como direção visual invariável.
- `data/project-health.json` e o `globals.css` não rastreado da raiz continuam fora deste escopo.
Se o gate final do hardening não estiver verde, a execução deste plano não começa. Não se adapta SEO ao código intermediário.
## Princípios e limites
### Sempre
- Uma URL canônica por intenção; páginas existentes são enriquecidas em vez de duplicadas.
- Conteúdo factual, legível no HTML e apoiado por fonte verificável.
- Base em Garuva e atendimento no Norte de Santa Catarina/remoto somente no alcance já declarado pelo site.
- `Person` é a entidade prestadora e autora; `WebSite` é o site; `ProfilePage` existe na rota de currículo; cada serviço tem `Service`; rotas não-home têm `BreadcrumbList` quando a trilha existe na interface.
- Metadata API e convenções de imagem do Next.js 16; sem pacote novo.
- CTAs usam os componentes/classes atuais. A separação é semântica e de destino, não um novo layout.
- Privacidade descreve exatamente o comportamento do consentimento entregue pelo hardening.
### Nunca
- Página-clone de Garuva, Joinville, Curitiba ou outra cidade.
- `llms.txt`, `FAQPage`, `keywords` em metadata, `ProfessionalService`, ou `LocalBusiness` sem negócio/local comprovado.
- Coordenada, endereço postal, preço, cliente, review, volume, ranking, resultado comercial ou métrica de performance não comprovados.
- Afirmação de que framework, schema ou formatação garante ranking/citação.
- Mudança de paleta, tipografia, grid, hero, efeitos preservados, arquitetura de informação ou biblioteca visual.
- Dependência, commit, branch ou ação em Search Console/Bing/Cloudflare nesta iniciativa de documentação.
## Arquitetura de conteúdo
`lib/site-content.ts` continua sendo a fonte das páginas de serviço e case. Não será criada uma CMS ou segunda camada de dados. A extensão mínima é:
```ts
export const intentCtas = {
hiring: {
label: "Avaliar perfil para contratação",
href: "/curriculo",
},
consulting: {
label: "Solicitar consultoria técnica",
href: "/#contact",
},
} as const;
export type CtaIntent = keyof typeof intentCtas;
export interface EvidenceSource {
id: string;
label: string;
url: string;
}
export interface SourcedStatement {
text: string;
sourceIds: string[];
}
export interface CaseStudyDetail {
answer: SourcedStatement;
challenge: SourcedStatement;
implementation: SourcedStatement[];
boundaries: SourcedStatement[];
sources: EvidenceSource[];
}
export interface ServicePageContent {
slug: string;
navLabel: string;
eyebrow: string;
seoTitle: string;
metaDescription: string;
headline: string;
answer: string;
intro: string;
audience: string[];
outcomes: string[];
process: string[];
stack: string[];
ctaIntent: "consulting";
ctaLabel: string;
ctaHref: string;
relatedCaseIds: string[];
faq?: { question: string; answer: string }[];
}
export interface CaseStudyContent {
id: string;
title: string;
subtitle: string;
summary: string;
impact: string;
stack: string[];
demoUrl?: string | null;
githubUrl?: string | null;
detail?: CaseStudyDetail;
}
```
`detail` é opcional porque somente três cases têm evidência suficiente nesta rodada. O conjunto deve ser exatamente `as-servicos`, `nutriscan` e `twins-lab`, sem depender da ordem. Afirmações técnicas/factuais em `answer`, `challenge`, `implementation` e limites comparativos exigem pelo menos um `sourceId` externo. O limite puramente editorial de AS Serviços mantém `sourceIds: []`: não será criada URL fictícia ou circular para uma decisão deste design. Qualquer ID presente deve resolver para uma fonte única dentro do mesmo case, e toda `EvidenceSource.url` deve ser um permalink GitHub `/blob/<SHA de 40 hex>/...`. A página exibe as fontes ao final do conteúdo aprofundado.
A relação existente `ServicePageContent.relatedCaseIds` permanece como única fonte de relacionamento. A função nova:
```ts
export function getRelatedServices(caseId: string): ServicePageContent[] {
return servicePages.filter(({ relatedCaseIds }) =>
relatedCaseIds.includes(caseId),
);
}
```
permite links recíprocos sem manter duas listas divergentes.
### CTAs
Os seis serviços mantêm `ctaIntent: "consulting"` e levam a `/#contact`, com rótulo específico de conversa técnica/projeto. As páginas de case mostram dois caminhos usando o sistema visual existente:
- **“Avaliar perfil para contratação”** → `/curriculo` (`hiring`);
- **“Solicitar consultoria técnica”** → `/#contact` (`consulting`).
O currículo mantém o caminho de contratação e oferece um link secundário para `/servicos` a quem procura consultoria. Não há formulário, modal ou estado de URL novo.
## Cases escolhidos e fonte de cada campo
### 1. AS Serviços (`as-servicos`)
Foi escolhido por ter demo pública, repositório público atual, README técnico, manifesto e implementação legível da cena.
| Campo novo | Texto factual autorizado | Fontes |
| --- | --- | --- |
| `detail.answer` | “Site institucional em Next.js 16 que representa quatro frentes de engenharia com uma cena Three.js gerada em código: uma casa wireframe e uma placa-mãe ligadas ao progresso da rolagem.” | `README.md`, `package.json`, `lib/three/engine.ts` |
| `detail.challenge` | “Representar engenharia física e software na mesma narrativa visual sem depender de um modelo 3D pronto.” | `README.md`, `lib/three/engine.ts` |
| `detail.implementation[0]` | “O motor monta casa, instalação elétrica e placa-mãe com geometrias e linhas do Three.js.” | `lib/three/engine.ts` |
| `detail.implementation[1]` | “O canvas converte a rolagem em progresso de 0 a 1 e o entrega ao motor da cena.” | `components/scene/scene-canvas.tsx` |
| `detail.implementation[2]` | “A qualidade pode cair para `static`, `low`, `medium` ou `high` conforme WebGL, reduced motion, economia de dados, conexão e capacidade do dispositivo.” | `lib/three/engine.ts` |
| `detail.boundaries[0]` | “O case demonstra implementação e decisões técnicas; não atribui geração de leads, ranking ou ganho de performance.” | Limite puramente editorial deste design; `sourceIds: []` |
Fontes imutáveis de referência:
- `https://github.com/evandrodevbr/as-servicos/blob/abeb9946a704139336942bfd41ad163ccfe1e685/README.md`
- `https://github.com/evandrodevbr/as-servicos/blob/abeb9946a704139336942bfd41ad163ccfe1e685/package.json`
- `https://github.com/evandrodevbr/as-servicos/blob/abeb9946a704139336942bfd41ad163ccfe1e685/lib/three/engine.ts`
- `https://github.com/evandrodevbr/as-servicos/blob/abeb9946a704139336942bfd41ad163ccfe1e685/components/scene/scene-canvas.tsx`
### 2. NutriScan (`nutriscan`)
Foi escolhido porque o código público confirma fluxos concretos de busca, validação e fallback, embora a documentação e o manifesto discordem sobre versão e persistência. O texto novo usa apenas o cruzamento verificável.
| Campo novo | Texto factual autorizado | Fontes |
| --- | --- | --- |
| `detail.answer` | “Aplicação web para consultar produtos alimentícios por nome ou código de barras, combinando cache JSON com a API Open Food Facts.” | `app/api/search/route.ts`, `app/api/products/[barcode]/route.ts`, `lib/openFoodFactsApi.ts` |
| `detail.challenge` | “Reduzir a dependência da API externa e manter um caminho explícito para buscas e consulta por código.” | `app/api/search/route.ts`, `app/api/products/[barcode]/route.ts` |
| `detail.implementation[0]` | “A busca consulta o cache primeiro, permite modo somente-cache e recorre ao Open Food Facts quando faltam resultados.” | `app/api/search/route.ts` |
| `detail.implementation[1]` | “A consulta por código remove caracteres não numéricos, aceita de 8 a 13 dígitos, consulta cache e usa a API externa como fallback.” | `app/api/products/[barcode]/route.ts` |
| `detail.implementation[2]` | “O wrapper cancela requisições após oito segundos e diferencia busca por nome de busca por código.” | `lib/openFoodFactsApi.ts` |
| `detail.boundaries[0]` | “A página não afirma PWA, MongoDB, histórico de usuários, disponibilidade, volume de produtos ou resultado de saúde, porque as fontes atuais não sustentam esse conjunto de forma consistente.” | `package.json`, `prisma/schema.prisma`, README e código comparados |
Fontes imutáveis de referência:
- `https://github.com/evandrodevbr/nutriscan/blob/ef0e6d4eca1f8588bb6981c1cfa58e4b7db1302b/app/api/search/route.ts`
- `https://github.com/evandrodevbr/nutriscan/blob/ef0e6d4eca1f8588bb6981c1cfa58e4b7db1302b/app/api/products/%5Bbarcode%5D/route.ts`
- `https://github.com/evandrodevbr/nutriscan/blob/ef0e6d4eca1f8588bb6981c1cfa58e4b7db1302b/lib/openFoodFactsApi.ts`
- `https://github.com/evandrodevbr/nutriscan/blob/ef0e6d4eca1f8588bb6981c1cfa58e4b7db1302b/package.json`
- `https://github.com/evandrodevbr/nutriscan/blob/ef0e6d4eca1f8588bb6981c1cfa58e4b7db1302b/prisma/schema.prisma`
- `https://github.com/evandrodevbr/nutriscan/blob/ef0e6d4eca1f8588bb6981c1cfa58e4b7db1302b/README.md`
### 3. Twins Lab (`twins-lab`)
Foi escolhido porque o repositório contém um simulador, persistência, autenticação e uma rota de streaming observáveis. As métricas promocionais e recursos marcados como futuros no README são excluídos.
| Campo novo | Texto factual autorizado | Fontes |
| --- | --- | --- |
| `detail.answer` | “Protótipo de gêmeo digital industrial com autenticação, simulador de reator, persistência MongoDB e streaming de eventos do servidor.” | `package.json`, `app/api/reactor/stream/route.ts`, `simulators/reactor/reactor-simulator.ts` |
| `detail.challenge` | “Expor estados simulados do reator a uma interface autenticada e manter histórico/status do simulador.” | `app/api/reactor/stream/route.ts`, `simulators/reactor/reactor-simulator.ts` |
| `detail.implementation[0]` | “A rota exige sessão do NextAuth antes de abrir o stream.” | `app/api/reactor/stream/route.ts` |
| `detail.implementation[1]` | “A resposta usa `text/event-stream`, envia estados/dados/erros em intervalos de dois segundos e limita a conexão a dez minutos.” | `app/api/reactor/stream/route.ts` |
| `detail.implementation[2]` | “O simulador salva dados e estado no MongoDB e notifica clientes quando execução ou cenário mudam.” | `simulators/reactor/reactor-simulator.ts` |
| `detail.boundaries[0]` | “O case não usa as alegações de economia, ROI, precisão, manutenção preditiva futura, equipamento real, WebSocket, OpenRouter ou atualização sub-segundo.” | README comparado ao código público |
Fontes imutáveis de referência:
- `https://github.com/evandrodevbr/twins-lab/blob/f83f99a7417f16ece0cd7fe4832ba122e7152d0c/package.json`
- `https://github.com/evandrodevbr/twins-lab/blob/f83f99a7417f16ece0cd7fe4832ba122e7152d0c/app/api/reactor/stream/route.ts`
- `https://github.com/evandrodevbr/twins-lab/blob/f83f99a7417f16ece0cd7fe4832ba122e7152d0c/simulators/reactor/reactor-simulator.ts`
- `https://github.com/evandrodevbr/twins-lab/blob/f83f99a7417f16ece0cd7fe4832ba122e7152d0c/README.md`
## Resposta factual cedo
A primeira descrição abaixo do H1 deve responder o que é oferecido ou demonstrado sem slogan genérico:
- Serviços usam o novo `answer` antes das listas de público/processo.
- Os três cases aprofundados usam `detail.answer.text` no hero; os demais mantêm o `summary` atual, após remoção de afirmações não comprovadas.
- Hubs explicam em uma frase o que o usuário encontra e para qual próximo passo cada cartão aponta.
- Currículo começa com profissão, base e modalidades já declaradas; não promete disponibilidade, prazo de resposta ou presença física nova.
Texto regional comum autorizado: **“Base em Garuva, atendimento a projetos no Norte de Santa Catarina e atuação remota.”** Ele substitui enumerações extensas de cidades onde a página não demonstra presença local.
## Modelo de entidades e dados estruturados
IDs canônicos, exportados por `lib/seo.ts`:
```ts
export const SEO_IDS = {
website: `${siteUrl}/#website`,
person: `${siteUrl}/#person`,
profilePage: `${siteUrl}/curriculo#profile-page`,
} as const;
```
### Grafo raiz
Somente:
```json
{
"@type": "WebSite",
"@id": "https://evandro.dev.br/#website",
"url": "https://evandro.dev.br",
"name": "Evandro.dev.br",
"inLanguage": "pt-BR",
"publisher": { "@id": "https://evandro.dev.br/#person" }
}
```
```json
{
"@type": "Person",
"@id": "https://evandro.dev.br/#person",
"name": "Evandro Fonseca Junior",
"url": "https://evandro.dev.br",
"image": "https://evandro.dev.br/edev.jpg",
"homeLocation": { "@type": "Place", "name": "Garuva, Santa Catarina, Brasil" },
"sameAs": ["GitHub público", "LinkedIn público"]
}
```
Não há endereço postal, coordenada, preço, raio, catálogo de ofertas ou Instagram divergente sem validação.
### Currículo
`ProfilePage` aparece somente em `/curriculo`, referencia `SEO_IDS.person`, define `isPartOf` para `SEO_IDS.website` e não inclui `dateModified` até existir uma data editorial persistida. A pessoa pode ser referenciada por `@id`; não é duplicada com propriedades divergentes.
### Serviço
Cada `/servicos/[slug]` emite:
- `Service` com `@id: ${pageUrl}#service`, `url`, `name`, `description`, `provider: { "@id": SEO_IDS.person }`;
- `areaServed` limitado a Garuva e Norte de Santa Catarina, sem coordenada/raio/endereço;
- `BreadcrumbList` com `@id: ${pageUrl}#breadcrumb` e posições contíguas 1–3.
### Hubs, cases e privacidade
Hubs preservam `CollectionPage`/`ItemList` quando os itens visíveis coincidem com a lista. Cases removem `Article` incompleto; usam `BreadcrumbList`, pois autoria/datas/imagem de artigo não estão comprovadas como publicação editorial. Currículo, hubs, cases, serviços e privacidade têm breadcrumbs visíveis/estruturados coerentes.
## Frescor
- `app/sitemap.ts` não inclui `lastModified` enquanto não houver data editorial estável por URL.
- `changeFrequency` e `priority` também são removidos: não há fonte operacional que os mantenha e mecanismos os tratam apenas como dicas.
- `/curriculo` remove `new Date().toISOString()`.
- Nenhuma data muda por build ou request.
Critério determinístico: duas chamadas consecutivas de `sitemap()` em instantes diferentes retornam estruturas profundamente iguais.
## Metadata e Open Graph por rota
Toda rota indexável tem title, description e canonical próprios. Nenhuma metadata contém `keywords`. Title e description refletem a intenção da rota e não enumeram cidades.
Open Graph usa `ImageResponse` e a convenção de arquivo do Next.js, com `1200×630`, `image/png`, alt específico e template visual compartilhado em `lib/og-card.tsx`. O template reutiliza preto, papel e verde ácido e apenas tipografia/texto; não introduz nova direção visual nem asset falso.
Arquivos por segmento:
- `app/opengraph-image.tsx` — marca/home;
- `app/servicos/opengraph-image.tsx` — hub de serviços;
- `app/servicos/[slug]/opengraph-image.tsx` — serviço selecionado pelos params;
- `app/cases/opengraph-image.tsx` — hub de cases;
- `app/cases/[slug]/opengraph-image.tsx` — case selecionado pelos params;
- `app/curriculo/opengraph-image.tsx` — perfil profissional;
- `app/privacidade/opengraph-image.tsx` — privacidade e consentimento.
Metadata dinâmica usa o mesmo conteúdo de `site-content.ts` que a página e a imagem; slug inválido retorna resposta 404 da imagem, sem imagem genérica enganosa. Twitter herda o mesmo card grande.
## Links recíprocos
- Cada cartão de case relacionado dentro de um serviço recebe link interno “Ler case: {título}” antes dos links externos de demo/código.
- Cada case lista os serviços encontrados por `getRelatedServices(caseId)` com anchors descritivos.
- O hub de cases mantém “Ler case”. O hub de serviços mantém “Abrir página”, sem link direto externo.
- Demo/GitHub continuam externos, com `rel="noreferrer"` ou `noopener noreferrer`.
- Estado de demo continua obedecendo `PortfolioProjectView`; SEO não reintroduz filtragem ou health fetch.
## Privacidade e consentimento
Criar `/privacidade` com metadata/canonical/OG próprios e conteúdo factual:
1. responsável e contato: Evandro Fonseca Junior, e-mail já publicado;
2. contato do formulário: os dados digitados são transformados em mensagem e abrem o WhatsApp; o site não possui endpoint próprio de armazenamento desse formulário;
3. analytics: antes de `granted`, nenhuma tag Google é carregada; a escolha fica em `localStorage` sob `evandro.analytics-consent.v1`; publicidade permanece negada; concessão pode criar cookies `_ga*`; revogação desativa novas coletas e expira cookies acessíveis conforme o hardening;
4. links externos: GitHub, LinkedIn, Instagram, demos e WhatsApp seguem políticas próprias;
5. alteração de preferência: usar o controle “Preferências de privacidade” entregue pelo hardening;
6. direitos/contato: solicitações são encaminhadas ao e-mail publicado;
7. data de vigência fixa: 2026-09-04.
A página não declara certificação jurídica, retenção que não foi configurada, compartilhamento que não foi verificado ou conformidade automática. O diálogo de consentimento e o Footer passam a ter link crawlable para `/privacidade`, usando estilos atuais.
## Documento operacional externo
A implementação cria `docs/seo-geo-operations.md` como runbook declarativo. Cada ação começa desmarcada e inclui evidência esperada, sem texto que sugira execução:
- Google Search Console: verificar propriedade, submeter sitemap, inspecionar as 17 URLs finais (as 16 atuais + privacidade), registrar canônica/cobertura e baseline de 90 dias, abrir relatórios de IA quando disponíveis na conta.
- Bing Webmaster Tools: verificar/importar propriedade, submeter sitemap, inspecionar URLs e registrar AI Performance; IndexNow fica fora até haver atualização frequente.
- Cloudflare: comparar `robots.ts` com resposta pública, Managed Robots, AI Crawl Control e WAF; registrar política separada de busca/citação e treino; conferir `200/403/429` em logs.
- Medição: p75 de LCP/INP/CLS por template em CrUX/RUM, consultas/landing/conversão, caminhos case→serviço→contato e citações reportadas pelas plataformas.
- Ações que exigem conta, publicação ou painel externo continuam humanas e consequenciais; o plano apenas documenta.
## Matriz de conflitos
| Área | Hardening/SEO envolvidos | Conflito | Regra |
| --- | --- | --- | --- |
| `app/layout.tsx` | Hardening Task 4; SEO entidades | Hardening achata providers sem tocar JSON-LD | SEO relê o arquivo final e altera apenas metadata/JSON-LD após Task 12. |
| `app/components/GoogleAnalytics.tsx` | Hardening Task 11; SEO privacidade | Consentimento ainda não existe no baseline atual | SEO consome a chave/estados finais e adiciona somente link/cópia coerente. |
| `app/components/Footer.tsx` | Hardening Task 9 ajusta testes; SEO adiciona privacidade | Teste e links podem mudar | Hardening primeiro; SEO preserva o contrato acessível final. |
| `lib/portfolio-data.ts`/JSON | Hardening Tasks 2–3; SEO cases | Tipo de projeto/health muda | SEO não altera o loader nem `PortfolioProjectView`; modifica apenas fatos dos três registros JSON. |
| `app/cases/[slug]/page.tsx` | SEO conteúdo/schema/metadata | Mesma rota em múltiplas preocupações | Uma única slice de case possui toda a rota; OG fica em arquivo irmão. |
| `app/servicos/[slug]/page.tsx` | SEO resposta/schema/links/CTA/metadata | Mesmo arquivo | Uma única slice de serviço possui toda a rota; OG fica em arquivo irmão. |
| `lib/site-content.ts` | Conteúdo factual, serviços e cases | Fonte compartilhada | Contrato e valores fecham na primeira tarefa; slices posteriores são somente consumidoras. |
| `lib/og-card.tsx` | Todas as imagens OG | Template compartilhado | Criar uma vez; arquivos de segmento só fornecem texto. |
| `app/sitemap.ts` | Frescor e nova rota privacidade | Task de entidades remove datas; privacidade adiciona URL | A task de privacidade adiciona a URL ao sitemap já sem datas; reler antes de editar. |
## Critérios observáveis de aceite
1. O gate completo do hardening está verde antes do primeiro arquivo de produção SEO ser alterado.
2. Duas execuções de `sitemap()` produzem a mesma saída e incluem 17 URLs canônicas sem `lastModified` artificial.
3. Nenhum HTML contém `ProfessionalService`, `LocalBusiness`, `FAQPage`, meta `keywords`, coordenada, preço ou endereço postal não comprovado.
4. O grafo raiz tem um `WebSite` e uma `Person`; `/curriculo` tem um `ProfilePage` que referencia a mesma pessoa; serviços têm `Service`; breadcrumbs têm posições contíguas e URLs canônicas.
5. Todas as rotas indexáveis têm title, description, canonical e OG coerentes; cada endpoint de imagem responde `200 image/png` em 1200×630 e alt específico.
6. Serviço → case e case → serviço são links internos reais com nome descritivo.
7. AS Serviços, NutriScan e Twins Lab exibem resposta, desafio, implementação, limites e fontes; nenhuma página atribui resultado comercial.
8. Demais cases não recebem profundidade genérica nem campos vazios.
9. A primeira descrição de cada serviço/case responde factualmente ao propósito da URL.
10. Texto regional usa base em Garuva, Norte de Santa Catarina e atuação remota, sem alegar escritórios ou presença por cidade.
11. CTAs de contratação levam a `/curriculo`; CTAs de consultoria levam a `/#contact`; ambos mantêm o sistema visual atual.
12. `/privacidade` descreve o consentimento final, está linkada pelo diálogo e Footer e não afirma ação/garantia jurídica inexistente.
13. O runbook externo registra ações e evidências esperadas sem marcar nenhuma como feita.
14. Desktop, mobile e reduced motion preservam direção visual, conteúdo, foco e fluxo de consentimento do hardening.
15. Jest focado, Biome, TypeScript, build e smoke em navegador encerram verdes.
## Riscos e mitigação
| Risco | Impacto | Mitigação |
| --- | --- | --- |
| Executar sobre hardening incompleto | Alto | Gate explícito e releitura dos arquivos finais compartilhados. |
| Duplicar ou divergir fatos entre JSON e `site-content.ts` | Alto | Atualizar somente os três cases na mesma tarefa e testar frases proibidas/links de fonte. |
| Fonte pública mudar | Médio | URLs com SHA fixo; tratar o case como retrato técnico daquela revisão. |
| Converter README promocional em fato | Alto | Usar somente código/manifests cruzados; registrar exclusões por case. |
| Schema válido sintaticamente, mas semanticamente incorreto | Alto | Grafo mínimo, propriedades visíveis e testes de IDs/referências/breadcrumbs. |
| OG aumentar duplicação | Médio | Um componente visual puro e arquivos de segmento mínimos. |
| Link de privacidade contradizer consentimento | Alto | Consumir literalmente chave/estados/comportamento final do hardening e testar juntos. |
| Texto regional virar doorway | Alto | Nenhuma nova rota de cidade; uma frase factual compartilhada. |
| CTA separar só a aparência | Médio | Destinos observáveis distintos: contratação `/curriculo`, consultoria `/#contact`. |
| Testes fixarem classes/copy incidental | Médio | Assertar rotas, links, schema, fontes e comportamentos; não classes ou wrappers. |
## Auto-revisão contra as 57 fontes
| Grupo do relatório | Quantidade | Como o design incorporou | Conflito evitado |
| --- | ---: | --- | --- |
| Google [G1–G13] | 13 | Conteúdo útil/original, canônicas, schema visível, sem frescor falso e medição em ferramentas próprias | Sem city-clone, escala artificial, schema especial de IA ou promessa de ranking. |
| Bing + Web Vitals [B1–B3, W1] | 4 | Resposta cedo, páginas focadas, evidência, runbook de AI Performance e p75 de campo | Sem IndexNow prematuro ou score de laboratório alegado como resultado. |
| Schema.org [S1–S6] | 6 | `Person`, `Service`, `ProfilePage`, `BreadcrumbList` mínimos e conectados | `ProfessionalService`, `LocalBusiness` e `FAQPage` excluídos. |
| Next.js [N1–N7] | 7 | Metadata por rota, `generateMetadata`, imagens por segmento e build estático | Sem nonce, pacote experimental ou alteração de CSP/analytics do hardening. |
| GEO/crawlers [A1–A2, C1–C4, L1] | 7 | Fontes visíveis e medição tratadas como experimento; política de bots só documentada | Sem `llms.txt`, allowlist executada ou causalidade de citação. |
| Mercado observado | 20 | Clareza de intenção, links, projetos e prova de primeira mão | Sem stuffing, endereço, testemunho, métrica ou autoridade inferida de concorrente. |
| **Total** | **57** | **Todos os grupos possuem decisão correspondente.** | **Nenhuma recomendação proibida foi reintroduzida.** |
A revisão também confirmou: oito slices bastam; não há dependência nova; os três cases têm fonte por campo técnico/factual, com exceção explícita apenas para o limite editorial de AS; ações externas não são executadas; e a direção visual permanece invariável.