feat: harden portfolio and add SEO foundations
This commit is contained in:
1 parent
b079afdd73
commit
62bcfbd8fa
121 files changed
+7769
-10469
No files matched your search
@@ -0,0 +1,267 @@
|
||||
# Design: endurecimento técnico do portfólio
|
||||
|
||||
**Status:** aprovado para planejamento; implementação não iniciada
|
||||
**Data:** 2026-09-04
|
||||
|
||||
## Objetivo
|
||||
|
||||
Reduzir fragilidade operacional, JavaScript global e código sem consumidores sem redesenhar o portfólio. O resultado mantém a identidade preto/papel/verde ácido, a tipografia e a narrativa visual, enquanto torna projetos, foco, movimento, dependências, analytics, deploy e validação previsíveis.
|
||||
|
||||
## Estado de checkout que faz parte do alvo
|
||||
|
||||
A implementação futura trabalhará no checkout atual, sem worktree. Essa decisão é deliberada porque as alterações não commitadas abaixo são parte do estado-alvo e haverá um único editor; a pesquisa paralela é somente leitura:
|
||||
|
||||
- `data/project-health.json` modificado pelo usuário: não regenerar nem reverter nesta iniciativa.
|
||||
- `lib/portfolio-data.ts` modificado pelo usuário: evoluir a partir do conteúdo atual, preservando a atualização apenas em desenvolvimento.
|
||||
- `globals.css` na raiz, não rastreado: não editar, mover, importar ou apagar. O stylesheet ativo é `app/globals.css`.
|
||||
- `lib/portfolio-data.load.test.ts` não rastreado: manter como baseline e atualizar suas expectativas somente quando o contrato de projetos mudar.
|
||||
|
||||
Não usar branch, commit, reset, checkout, stash ou comandos Git destrutivos. Rollback será por arquivo e por tarefa, usando os snapshots lidos antes de cada edição.
|
||||
|
||||
## Escopo e arquitetura atual observada
|
||||
|
||||
- Next.js App Router com React 19 e TypeScript estrito.
|
||||
- `app/page.tsx` carrega projetos no servidor por `loadLiveFeaturedProjects()` e repassa dados para `CinematicHome`.
|
||||
- `filterLiveProjects()` hoje transforma um snapshot ausente/expirado em lista vazia, ocultando todo projeto.
|
||||
- `app/layout.tsx` envolve todas as rotas em Lenis/GSAP, contexto de animação, transição de rota e overlays; `PageIntro` cria um portal modal que cobre a página por tempo fixo.
|
||||
- A home soma motion local, GSAP/ScrollTrigger redundante, cursores/overlays e loops contínuos de canvas.
|
||||
- `GoogleAnalytics` carrega GA em produção sem consentimento e usa rewrites que camuflam script/coleta.
|
||||
- `next.config.ts` marca diretórios públicos com nomes estáveis como `immutable` e mantém CSP somente em relatório com `unsafe-inline` e `unsafe-eval`.
|
||||
- Não existe workflow em `.github/workflows`.
|
||||
- `pnpm-lock.yaml` resolve Next `16.2.9`, contém overrides históricos e foi gerado com faixas, embora `package.json` não fixe o conjunto de dependências.
|
||||
- `nixpacks.toml` já escolhe Node 22 e pnpm 9, mas instala com `--no-frozen-lockfile`.
|
||||
- Há componentes e testes sem consumidores de produção, incluindo dois caminhos de contato, biblioteca de demos/magicui e infraestrutura de animação não consumida.
|
||||
|
||||
## Mapa de capacidades
|
||||
|
||||
| Módulo | Responsabilidade | Depende de |
|
||||
| --- | --- | --- |
|
||||
| Projetos e saúde de demo | Sempre fornecer conteúdo; anexar estado observacional do link | JSON estático, snapshot opcional |
|
||||
| Shell acessível | Entregar conteúdo/foco sem overlay bloqueante e sem scroll engine global | layout, CSS global |
|
||||
| Movimento e performance | Preservar efeitos essenciais, remover duplicação e pausar trabalho invisível | hooks locais, Intersection Observer, Page Visibility |
|
||||
| Dependências e deploy | Fixar runtime/dependências e produzir lock reproduzível | código morto já removido |
|
||||
| Analytics e headers | Consentimento opt-in, revogação e carregamento direto de GA | layout, `next/script`, configuração de headers |
|
||||
| CI | Instalação congelada e gates determinísticos | lock final e scripts do pacote |
|
||||
| Documentação operacional | Descrever apenas comandos e comportamento realmente presentes | implementação concluída |
|
||||
|
||||
Direção de dependência: conteúdo estático → projeção com saúde → home; shell/CSS → componentes; código vivo → manifesto de dependências → lock/deploy/CI. Nenhum componente de UI consulta rede para decidir se conteúdo existe.
|
||||
|
||||
## Decisões
|
||||
|
||||
### 1. Projeto é conteúdo; saúde é apenas estado do link
|
||||
|
||||
Substituir o contrato de “projetos vivos” por uma projeção que mantém todo projeto com `featured !== false` e anexa o estado da demo:
|
||||
|
||||
```ts
|
||||
export type DemoAvailability = "available" | "unavailable" | "unknown" | "none";
|
||||
|
||||
export type PortfolioProjectView = PortfolioProject & {
|
||||
demoAvailability: DemoAvailability;
|
||||
demoCheckedAt: string | null;
|
||||
};
|
||||
```
|
||||
|
||||
Regras:
|
||||
|
||||
1. `featured === false` continua sendo a única regra de exclusão editorial.
|
||||
2. Sem `demoUrl`, o estado é `none` e não há CTA de demo.
|
||||
3. Sem entrada de snapshot, o estado é `unknown`; o card permanece e o URL configurado não é descartado.
|
||||
4. Resposta final `200` com `Content-Type` HTML produz `available`.
|
||||
5. Qualquer resultado explícito diferente produz `unavailable`.
|
||||
6. `unavailable` renderiza a evidência inteira e um controle não navegável com texto “Demo indisponível na última verificação”.
|
||||
7. `unknown` mantém o link e o anota como “Disponibilidade não verificada”.
|
||||
8. Em desenvolvimento, snapshot ausente ou mais antigo que quinze minutos pode disparar uma atualização em memória; falha de rede retorna os projetos com o último estado conhecido/`unknown`.
|
||||
9. Em produção, render nunca chama demos externas. O snapshot versionado é observacional e conteúdo não depende de cron, validade temporal ou rede.
|
||||
|
||||
`app/page.tsx` deixa de usar ISR de cinco minutos para simular monitoramento. `scripts/check-projects.mjs` continua como comando manual de atualização atômica, não como requisito de disponibilidade do site.
|
||||
|
||||
### 2. Acessibilidade não depende de animação
|
||||
|
||||
- Remover `PageIntro` e seu teste; não substituir por outro splash.
|
||||
- Manter o skip link e garantir que nenhum overlay o cubra.
|
||||
- Colocar o reset de cor de links na camada base do Tailwind, permitindo que `text-white`, `text-slate-*` e estados hover da 404 prevaleçam.
|
||||
- Em `prefers-reduced-motion: reduce`, desativar smooth scroll e reduzir animações/transições CSS a uma atualização praticamente instantânea.
|
||||
- Fallbacks estáticos permanecem com o mesmo conteúdo e ações essenciais.
|
||||
|
||||
### 3. Um efeito por intenção, não camadas redundantes
|
||||
|
||||
A assinatura visual preservada é: paleta, fontes, hero “command center”, globo, starfield, notebook 3D quando elegível, cartões, divisores e tipografia de marquee.
|
||||
|
||||
Serão removidos:
|
||||
|
||||
- Lenis e ticker GSAP do layout global.
|
||||
- Transição de rota global.
|
||||
- `AnimationProvider`, pois nenhum componente de produção consome seu contexto.
|
||||
- `ReelDirector` e `CinematicSection`, que repetem revelações já feitas por componentes locais.
|
||||
- Cursor customizado e seu `MutationObserver`, que reaplica listeners sobre a árvore.
|
||||
- canvas de film grain; a textura passa a CSS estático.
|
||||
- loops JS de marquee e divisores; tornam-se animações CSS/SVG sem estado React por frame.
|
||||
|
||||
GSAP/ScrollTrigger permanece somente na história do notebook, onde controla uma timeline scroll-driven real. Motion permanece nos componentes que já implementam feedback/reveal local. Nenhuma biblioteca nova será adicionada.
|
||||
|
||||
Loops restantes obedecem aos dois sinais:
|
||||
|
||||
```ts
|
||||
const shouldRun = isIntersecting && !document.hidden && !shouldReduceMotion;
|
||||
```
|
||||
|
||||
`HeroStarfield` já segue esse contrato e será preservado. `NotebookStory` combinará interseção e `visibilitychange`; o globo manterá flags separadas para interseção e visibilidade do documento para não reativar fora da viewport.
|
||||
|
||||
### 4. Hero terá somente a variante ativa
|
||||
|
||||
`Hero` mantém exclusivamente “command-center”. Remover pesos, seleção aleatória, cinco blocos editoriais não alcançáveis, layouts alternativos e branches de `SideVisual`. `HeroScene` perde o parâmetro `variant`, hoje ignorado. O conteúdo visível, CTAs, globo, telemetria, links sociais e fallback de reduced motion permanecem.
|
||||
|
||||
### 5. Remoção de código morto é um corte limpo
|
||||
|
||||
Excluir, após confirmar todos os callers na execução:
|
||||
|
||||
- `ContactForm`, `ContactInfo` e `usePortfolioData`; `Contact` é o caminho ativo.
|
||||
- `CompanyLogos` e seus três arquivos de teste.
|
||||
- todos os demos na raiz de `components/` e `components/magicui/`, sem importadores.
|
||||
- `AnimationProvider`, seu teste, `app/lib/animations/config.ts`, `app/lib/animations/variants.ts` e `app/hooks/useScrollReveal.ts`.
|
||||
- `FloatingElements`, `HomeButton`, `HorizontalScroll` e `CinematicText`, sem importadores.
|
||||
- componentes de performance substituídos/removidos na decisão 3 e seus testes de implementação órfãos.
|
||||
|
||||
Não manter aliases, reexports ou shims.
|
||||
|
||||
### 6. Testes protegem comportamento observável
|
||||
|
||||
- Reescrever os testes de saúde para provar que todos os projetos editoriais permanecem e apenas o estado da demo muda.
|
||||
- Preservar um teste de desenvolvimento que prova atualização em memória sem permitir fetch em produção.
|
||||
- Cobrir o CTA disponível, indisponível e desconhecido de `Projects` por nome/estado acessível.
|
||||
- Remover PBTs de `CompanyLogos` junto do componente.
|
||||
- Remover PBTs do Footer que repetem um componente estático; `Footer.test.tsx` já cobre nomes acessíveis e segurança de links.
|
||||
- Substituir três PBTs de `Experience` por testes determinísticos de filtro editorial e ordem cronológica; hierarquia de heading não precisa de geração aleatória.
|
||||
- Remover a asserção que fixa classes internas do Footer e a asserção da hero que inspeciona props serializadas de motion.
|
||||
- Remover `fast-check` quando seu último consumidor desaparecer.
|
||||
|
||||
### 7. Runtime e dependências serão reproduzíveis
|
||||
|
||||
- `engines.node`: `22.x`.
|
||||
- `packageManager`: `[email protected]`.
|
||||
- Next.js: `16.3.0`, menor versão estável da mesma major que eliminou as três vulnerabilidades high mantidas por `16.2.11` e `16.2.12`.
|
||||
- Todas as dependências diretas e de desenvolvimento terão versão exata, sem `^`, `~` ou comparadores.
|
||||
- `@types/node` acompanha o runtime em `22.20.1`.
|
||||
- `@types/three` passa para `devDependencies`.
|
||||
- Remover pacotes cujo último consumidor foi apagado: `@gsap/react`, `class-variance-authority`, `lenis`, `ogl`, `tailwindcss-animate`, `tw-animate-css`, `fast-check`, `@types/resize-observer-browser` e `resize-observer-polyfill`.
|
||||
- Manter GSAP, cobe, Motion, React Three Fiber, Drei e Three porque ainda há consumidores ativos.
|
||||
- Regenerar `pnpm-lock.yaml` com pnpm 9.15.9 e sem bloco `overrides`.
|
||||
- Corrigir advisories atualizando o pacote direto proprietário da cadeia; não usar `audit --force`, exceções ou novos overrides.
|
||||
- Nixpacks usa `pnpm install --frozen-lockfile`.
|
||||
|
||||
### 8. CI terá um caminho único
|
||||
|
||||
Criar `.github/workflows/ci.yml` para `push` e `pull_request`, em Ubuntu, Node 22 e pnpm 9.15.9. Ordem:
|
||||
|
||||
1. checkout;
|
||||
2. instalação congelada com cache do pnpm;
|
||||
3. `pnpm lint`;
|
||||
4. `pnpm typecheck`;
|
||||
5. `pnpm test -- --runInBand`;
|
||||
6. `pnpm build`;
|
||||
7. `pnpm audit --prod --audit-level=high`;
|
||||
8. `pnpm audit --dev --audit-level=high`.
|
||||
|
||||
Não adicionar Playwright, Cypress ou outro browser ao projeto. O smoke visual/interativo usa o Chromium disponível no harness durante a implementação; CI fica restrita aos gates aprovados.
|
||||
|
||||
### 9. Analytics exige opt-in e permite revogação
|
||||
|
||||
Aplicar consentimento a todos os visitantes, sem geolocalização de IP: é menor, previsível e não arrisca carregar GA antes de uma decisão quando a obrigação legal se aplica.
|
||||
|
||||
- `GoogleAnalytics` começa sem `<Script>` e sem requisição a Google.
|
||||
- Persistir somente `"granted"` ou `"denied"` em `localStorage` sob uma chave versionada.
|
||||
- Exibir ações “Aceitar analytics” e “Recusar” quando não há escolha.
|
||||
- Manter um controle “Preferências de privacidade” acessível após a escolha.
|
||||
- Somente `granted` monta `https://www.googletagmanager.com/gtag/js?id=G-QTT9QT0T3S` e a configuração de GA.
|
||||
- Consentimento de publicidade permanece negado; somente `analytics_storage` pode ser concedido.
|
||||
- Revogação chama `gtag("consent", "update", ...)` com estados negados, define `window["ga-disable-G-QTT9QT0T3S"] = true`, expira cookies `_ga` e `_ga_*` visíveis no domínio atual e desmonta o loader.
|
||||
- Remover todos os rewrites de analytics/ad-block bypass e usar endpoints oficiais diretamente.
|
||||
|
||||
A ordem segue a orientação oficial do Google: default negado antes de coleta, update após escolha e persistência/revogação controladas pela aplicação.
|
||||
|
||||
### 10. CSP e cache mudam somente quando compatíveis
|
||||
|
||||
Não introduzir nonces nesta rodada. A documentação do Next informa que nonces exigem renderização dinâmica, desabilitam otimização estática/ISR e alteram cache/custo; isso conflita com a home estática e seria endurecimento desproporcional.
|
||||
|
||||
- Manter CSP em `Report-Only`.
|
||||
- Adicionar `default-src 'self'` e `form-action 'self'`.
|
||||
- Remover `unsafe-eval` em produção; mantê-lo somente em desenvolvimento, onde o Next documenta a necessidade para debugging.
|
||||
- Manter `unsafe-inline` por enquanto para compatibilidade observada com scripts/estilos do Next e JSON-LD inline.
|
||||
- Manter origens Google apenas porque GA opt-in ainda as usa.
|
||||
- Remover regras `immutable` para `/static`, `/fonts`, `/assets` e `/logos`; esses nomes públicos não têm hash garantido. Assets gerados pelo Next em `/_next/static` já recebem cache apropriado do framework.
|
||||
|
||||
### 11. Documentação será derivada do estado final
|
||||
|
||||
- README troca badge Next 15 por 16.3.0, remove Lenis e bibliotecas apagadas, documenta Node 22/pnpm 9.15.9, scripts reais, arquitetura e validação.
|
||||
- `docs/project-health-cron.md` será substituído por `docs/project-health.md`, descrevendo snapshot opcional, atualização manual/desenvolvimento e a garantia de cards sempre visíveis.
|
||||
- Deployment documentado no README usa somente `nixpacks.toml`: Node 22, pnpm 9 e lock congelado.
|
||||
- Nenhum dado comercial, cliente, métrica, review, endereço ou claim novo será criado.
|
||||
|
||||
## Não-objetivos
|
||||
|
||||
- Redesenhar hero, paleta, tipografia, navegação ou arquitetura de informação.
|
||||
- Remover notebook 3D, React Three Fiber, Drei, Three, cobe, Motion ou GSAP usado pelo notebook.
|
||||
- Criar backend, banco, CMP externo ou coleta analytics própria.
|
||||
- Migrar para outro gerenciador de pacotes ou outra plataforma de deploy.
|
||||
- Alterar conteúdo, metadata, JSON-LD, sitemap ou robots por pesquisa SEO/GEO externa. Essa integração terá spec e plano separados.
|
||||
- Atualizar o snapshot do usuário em `data/project-health.json`.
|
||||
- Importar ou consolidar o `globals.css` não rastreado da raiz.
|
||||
- Endurecer CSP com nonce/hash sem uma prova separada de compatibilidade e impacto de renderização.
|
||||
|
||||
## Riscos e mitigação
|
||||
|
||||
| Risco | Impacto | Mitigação |
|
||||
| --- | --- | --- |
|
||||
| Saúde antiga desabilita uma demo já recuperada | Médio | Exibir timestamp/estado como última verificação e manter atualização manual/dev; nunca ocultar card |
|
||||
| Alterar o tipo de projeto quebra notebook/home | Alto | Introduzir tipo por interseção compatível, migrar callers no mesmo checkpoint e rodar testes focados |
|
||||
| Remoção de wrappers muda composição/hidratação | Alto | Remover um wrapper por corte, verificar foco, rotas e console no browser após o shell |
|
||||
| Remoção de efeitos achata identidade | Médio | Preservar hero command center, globo, starfield, notebook, Motion local e CSS visual; comparar desktop/mobile |
|
||||
| CSS reduced motion excessivamente amplo oculta conteúdo | Alto | Testar com emulação reduce; regras mudam duração, não `display`/`opacity` final |
|
||||
| Revogação não interrompe GA já carregado | Alto | Atualizar consent mode para denied, ativar flag `ga-disable`, expirar cookies e testar ausência de novas requisições após revoke |
|
||||
| CSP passa a bloquear runtime/GA | Alto | Permanecer Report-Only e remover apenas `unsafe-eval` em produção; observar console/network antes de enforcement futuro |
|
||||
| Lock regenerado reintroduz advisory | Alto | Auditar prod/dev após frozen install e atualizar cadeia proprietária sem overrides/force |
|
||||
| Exclusão alcança arquivo ainda importado | Alto | Buscar referências antes de cada lote e exigir `tsc` no checkpoint de corte |
|
||||
| Alterações do usuário são sobrescritas | Alto | Tratar quatro arquivos listados como baseline; não tocar nos dois fora de escopo e usar edições ancoradas nos dois em escopo |
|
||||
|
||||
## Rollback
|
||||
|
||||
Rollback é incremental, sem Git destrutivo:
|
||||
|
||||
1. Antes de cada tarefa, ler e registrar os arquivos exatos daquele lote.
|
||||
2. Se o checkpoint falhar, interromper o lote seguinte.
|
||||
3. Restaurar somente os arquivos do lote com edições inversas baseadas no snapshot pré-tarefa.
|
||||
4. Para arquivos novos, removê-los somente se foram criados pelo lote e ainda não são consumidos.
|
||||
5. Para dependências, restaurar juntos `package.json`, `pnpm-lock.yaml`, `tailwind.config.js` e `nixpacks.toml`; nunca restaurar apenas o lock.
|
||||
6. Reexecutar o último checkpoint verde.
|
||||
7. Nunca incluir `data/project-health.json`, `globals.css` da raiz ou mudanças não relacionadas no rollback.
|
||||
|
||||
## Critérios observáveis de aceite
|
||||
|
||||
1. Home e `/cases` continuam exibindo cards independentemente de snapshot ausente, antigo ou com falha.
|
||||
2. Demo explicitamente indisponível não navega e tem estado acessível; estado desconhecido não remove o URL nem o card.
|
||||
3. Produção não faz fetch de demos durante render; desenvolvimento pode atualizar em memória e falha de rede não esvazia conteúdo.
|
||||
4. Skip link recebe foco visível imediatamente, sem splash sobreposto.
|
||||
5. Links da 404 exibem as cores Tailwind pretendidas e foco visível.
|
||||
6. Reduced motion mostra todo conteúdo essencial sem scroll suave, loops ou transições longas.
|
||||
7. Loops restantes deixam de atualizar quando seção está fora da viewport ou `document.hidden` é verdadeiro.
|
||||
8. Hero mostra o mesmo conteúdo command-center sem infraestrutura de variantes mortas.
|
||||
9. Não há imports para os arquivos excluídos e TypeScript resolve o workspace sem erros.
|
||||
10. `package.json` contém versões exatas, Node `22.x`, pnpm `9.15.9` e Next `16.3.0`; lock não contém `overrides`.
|
||||
11. Instalação Nixpacks e local usa frozen lockfile com pnpm 9.
|
||||
12. `pnpm audit --prod --audit-level=high` e `pnpm audit --dev --audit-level=high` encerram com código zero, sem force, ignore ou override.
|
||||
13. Antes de consentimento/rejeição não há request a `googletagmanager.com`/`google-analytics.com` nem cookie `_ga*` criado pela página.
|
||||
14. Aceite carrega GA diretamente; revogação nega consentimento, desabilita coleta futura e expira cookies acessíveis.
|
||||
15. Configuração não contém rewrites de analytics; CSP continua Report-Only compatível e cache immutable não cobre arquivos públicos sem hash.
|
||||
16. CI executa install congelado, Biome, tsc, Jest, build e auditorias prod/dev.
|
||||
17. README e documentação de project health descrevem comandos, versões e comportamento finais sem depender de cron.
|
||||
18. `globals.css` da raiz e `data/project-health.json` permanecem inalterados.
|
||||
|
||||
## Fontes técnicas
|
||||
|
||||
- Next.js, CSP: https://nextjs.org/docs/app/guides/content-security-policy
|
||||
- Next.js, `next/script`: https://nextjs.org/docs/app/api-reference/components/script
|
||||
- Google, consent mode: https://developers.google.com/tag-platform/security/guides/consent
|
||||
- Google, API `gtag`: https://developers.google.com/tag-platform/gtagjs/reference#consent
|
||||
- Nixpacks, provider Node: https://nixpacks.com/docs/providers/node
|
||||
- pnpm, audit: https://pnpm.io/cli/audit
|
||||
- pnpm Action para pnpm 10 e anteriores: https://github.com/pnpm/action-setup
|
||||
@@ -0,0 +1,391 @@
|
||||
# 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.
|
||||
Reference in new issue
Block a user