Gates de Paridade (parity-gates)
Construa a baseline uma vez a partir de capture/, então aplique os gates em cada build até que todos os gates passem. Os gates leem apenas baseline/, nunca o site ao vivo, então uma build ainda pode ser verificada depois que o host antigo desapareceu.
A regra de ouro
A build padrão equivale ao site ao vivo, e um gate nunca é amenizado. Nenhuma flag transforma uma falha em um aviso. Mais quatro que decorrem disso:
- Nunca edite
baseline/manualmente. A única exceção:keepembaseline/scripts.json, que é como um fornecedor é removido (defina comofalsee registre a decisão). - Uma diferença é uma entrada, não uma edição. Inevitável:
exceptions.json. Uma melhoria:proposals.json, ativo apenas comPROPOSALS=on. Ambos estão emreferences/exceptions.md. - Um pulo não é uma aprovação. Um gate que não pôde verificar algo diz
N pulado (motivo)em sua linha de resumo. Reporte cada pulo. - Reporte um gate errado como um bug do kit; nunca altere o gate no site, nem na baseline.
Executando os scripts
Um site criado por convert-site tem os scripts em scripts/; execute-os a partir da raiz do site como os npm scripts abaixo, opções após --. Fora de um site scaffolded, execute a forma node mostrada sob cada etapa a partir da raiz do site, onde <skill-dir> é o caminho absoluto da pasta desta habilidade em uma cópia do kit onde npm install foi executado. Cada script imprime --help; execute-o em vez de ler o código-fonte. Saída 0 para aprovação, 1 para gate falhou ou desvio (drift), 2 para uso ou entrada faltando.
1. Baseline (uma vez)
npm run baseline
node <skill-dir>/scripts/build-baseline.mjs --capture capture --out baseline # fallback
Ela precisa de capture/inventory.json, media.json (capture's media.mjs) e templates.json (templates.mjs), e sai com código 2 nomeando a etapa a ser executada quando uma está faltando. Ela escreve baseline/{routes,seo,redirects,media,forms,scripts,meta,counts}.json e baseline/text/, e redirects.csv na raiz do site. Um shortlink sem destino é omitido e nomeado no stderr. Cada hop intermediário de uma cadeia capturada ganha sua própria linha direta ao destino final (/old-services → /old-services/ → /services/ também dá /old-services/,/services/, note hop of /old-services), a menos que esse hop seja em si uma rota construída, então um link para qualquer URL da cadeia chega em um hop.
Commite baseline/ e redirects.csv juntos. --check recontabiliza baseline/ contra counts.json (saída 1 em caso de desvio). A forma de cada arquivo está em references/contracts.md.
2. Redirecionamentos
redirects.csv (from,to,status,source,note) é a única fonte dos redirecionamentos do site. source é crawl, sitemap, shortlink ou seed da captura; uma linha que você adiciona é decision, seu note nomeando o id da decisão. Nunca edite manualmente a saída compilada; compile:
npm run redirects:build
node <skill-dir>/scripts/build-redirects.mjs --csv redirects.csv --host vercel --out vercel.json --trailing-slash always # fallback
--host cloudflare --out public/_redirects para Pages (Astro copia para dist/). Vercel: a chave redirects é substituída, toda outra chave é mantida, e trailingSlash é removido e escrito como as próprias regras de slash 308 do compilador após as do site, então uma fonte sem barra ainda é um hop. Apenas um caminho terminando em uma extensão de arquivo estático conta como um arquivo para essas regras, em qualquer letra maiúscula/minúscula: os tipos web, os tipos de documento, mídia e arquivo que uma pasta de upload armazena (docx, xlsx, csv, mov, wav, heic, gz, ...), e toda extensão encontrada em dist/ (senão public/), então reexecute-o após a build; /upgrade-guide/3.5/ é uma página e mantém sua barra. Um arquivo construído que as regras ainda redirecionariam falha no gate de redirecionamentos como arquivo construído redirecionado.
Cloudflare: uma linha de query (/?p=7) não pode ser correspondida por _redirects e vai para cloudflare-query-redirects.csv, para ser criado como um Single Redirect (um item "precisa de você"). Uma linha de query cujo destino é seu próprio caminho (/?p=2 para /) é uma não-operação e é omitida em ambos os hosts.
Uma linha capturada (qualquer source exceto decision) é uma URL ao vivo e não contém parâmetros: para Vercel, cada : e * nela é escapado (/foo:bar/ é escrito /foo\:bar/, e o mesmo para /trackshttps://www.example.com/x/, um href ao vivo quebrado que a captura seguiu); // é mantido. Apenas uma linha decision usa parâmetros :name e splats *, e mesmo assim um : antes de um dígito (uma porta) é escapado. Vercel rejeita um : simples, e o mesmo faz a simulação dos gates. Uma fonte sem forma válida (sem / inicial, espaço em branco, caractere de controle; no Cloudflare também um :<name> ou * capturado, que _redirects não pode escapar) é descartada, nomeada no stderr e listada como { source, reason } em redirects-skipped.json. Esse arquivo fica ao lado de redirects.csv (raiz do site), nunca sob public/, então não é implantado. O gate de redirecionamentos e verify-live contam uma linha listada como pulada, nunca aprovada. Quando o caminho simplesmente não deve ter redirecionamento, registre uma exceção "sem redirecionamento" (seção 4): { "kind": "redirect", "path": "<source>", "reason": "...", "action": "none", "approvedBy": "...", "date": "..." }.
3. Verificar
npm run build
npm run verify -- --endpoint https://forms.example.com/f/abc --thank-you /thank-you/
node <skill-dir>/scripts/verify/run-all.mjs --endpoint https://forms.example.com/f/abc --thank-you /thank-you/ # fallback
run-all executa os dez gates em ordem alfabética, dá a cada um apenas as opções que aceita, e para na primeira falha. Opções compartilhadas: --paths (lista separada por vírgulas, * corresponde a qualquer coisa), --json, --origin (links, seo, sitemaps: o host para o qual a build é), --allow-extra (rotas), --endpoint, --honeypot, --thank-you (formulários; --endpoint é obrigatório quando a baseline tem um formulário POST), --host, --config (redirecionamentos, links), --csv (redirecionamentos), --media-plan (mídia). Corrija a primeira falha, reconstrua, reexecute. Um gate sozinho: node <skill-dir>/scripts/verify/<gate>.mjs.
| Gate | Passa quando | Um pulo significa |
|---|---|---|
forms | Cada formulário ao vivo está em sua página com os mesmos nomes de campos em ordem e método; a ação de um formulário POST é --endpoint e ele carrega --honeypot; um formulário GET (uma caixa de busca) mantém o caminho da ação ao vivo e não precisa de nenhum ("N formulários GET/search comparados"); um formulário POST cuja ação ao vivo está em outro domínio registrável (um host de lista de newsletter; www, http, subdomínios e manipuladores WordPress como admin-ajax.php ou ?wpcf7 contam como o site) é mantido como está, comparado como um formulário GET pela URL completa da ação, e não precisa de --endpoint ("N formulários mantidos como estão (postam para )") | Sem --thank-you: a página de agradecimento dos formulários POST não foi verificada |
headers | A configuração do host envia nosniff, Referrer-Policy, X-Frame-Options ou CSP frame-ancestors em cada caminho, e, quando não indexável, X-Robots-Tag: noindex em cada caminho ou (Vercel) o meta robots noindex em cada página construída; uma regra de host de preview (https://*.pages.dev/*) conta apenas para o host de preview; com PUBLIC_INDEXABLE=true noindex em cada caminho é proibido | |
links | Cada href e src interno resolve para uma página ou arquivo construído, uma fonte de redirecionamento ou mídia da baseline, ou chega a uma página construída em um hop através das regras compiladas (vercel.json ou dist/_redirects: as regras de slash e cada linha de redirects.csv), como foi redirecionado ao vivo ("N links resolvidos através de redirecionamentos"); dois hops ou um destino ausente falham; sem configuração de host, /about onde a build serve /about/ falha | |
media | Cada arquivo de mídia da baseline é servido pela build com os mesmos bytes (--paths seleciona um arquivo por seu caminho ou por uma página que o referencia): em dist/ no modo de plano repo; sob o prefix do plano, no manifesto da loja (store) ou ainda ao vivo (live-temporary); fora do prefixo, em dist/ em todos os modos. Com PUBLIC_INDEXABLE=true, live-temporary falha para cada arquivo sob o prefixo (uma build de release não pode servir mídia do site ao vivo; um plano sem prefixo cobre cada arquivo) e ainda verifica os arquivos fora dele em dist/ | Modo de plano live-temporary em uma build de staging: arquivos sob o prefixo são servidos ao vivo, não verificados |
redirects | Cada redirecionamento da baseline e linha do csv responde em um hop com o mesmo status, e o destino responde 200 em dist/ (ou é externo, ou está sob um prefixo em baseline/meta.json excluded: então apenas o primeiro hop é verificado); nenhuma fonte é também um arquivo construído; nenhuma página ou arquivo construído é redirecionado de sua própria URL (página construída redirecionada pela regra <rule>; nenhuma exceção a cobre, corrija a regra); um caminho com uma exceção "sem redirecionamento" (kind: "redirect", action: "none") não é redirecionado de forma alguma | Uma linha de query sem operação, qualquer linha de query no Cloudflare, ou uma fonte sem forma válida que redirects-skipped.json lista com um motivo (não compilado) |
routes | Cada página, post, archive e feed em 200 é construído, rotas noindex ainda dizem noindex, nada extra é construído (os próprios scripts da captura em baseline/js.json e folhas de estilo em baseline/css.json em seus caminhos ao vivo, os nomes de sitemap ao vivo em baseline/sitemaps.json, e o conjunto de ícones de lançamento não são extras); uma rota capturada em qualquer status diferente de 200, 3xx, 404 ou 410 falha como não capturada: <status> a menos que uma exceção routes a pule ou nomeie seu substituto construído | Uma exceção routes com to: "skip": essa rota nunca foi capturada |
scripts | Cada fornecedor mantido está exatamente em suas páginas ao vivo com os mesmos ids; um removido em nenhuma; nenhum novo fornecedor | |
seo | Title, description, canonical, robots, lang, h1, h2, og e JSON-LD @types iguais por página; URLs do site na origem do site. A menos que PUBLIC_INDEXABLE=true, robots deve carregar o staging noindex, nofollow e o resto dele igual ao ao vivo além desses dois; uma build de release compara robots exatamente | |
sitemaps | Cada sitemap top-level em dist/ (o sitemap-index.xml do Astro, ou um nome ao vivo em baseline/sitemaps.json como wp-sitemap.xml ou sitemap_index.xml) lista exatamente as páginas 200 indexáveis na origem (escapes de porcentagem comparam sem distinção de maiúsculas/minúsculas: %c3 é %C3); cada linha Sitemap: do robots.txt está na origem e nomeia um arquivo em dist/, ou é uma linha que o robots.txt ao vivo (capture/robots.txt) carregava literalmente; cada feed é construído com tantos itens | |
text | O texto visível de cada página é igual a baseline/text/ (diff de palavras, 5 palavras de contexto) |
Uma captura feita com --exclude registra seus prefixos de caminho em baseline/meta.json excluded: o gate links não verifica um link sob um deles (como um link para outro host, observado "N links sob caminhos excluídos... não verificados"), e o gate sitemaps aceita uma linha Sitemap: do robots.txt sob um deles quando o robots.txt ao vivo a carregava literalmente (um sitemap de sub-site de multisite no mesmo host).
4. Diferenças e decisões
Quando a build não pode ou não deve corresponder ao site ao vivo, leia references/exceptions.md. Resumindo: uma entrada é { path, field, from, to, reason, approvedBy, date }; from: "" registra um valor que a build adiciona, to: "" um que ela remove; uma exceção que não corresponde mais, ou nomeia um caminho que o gate não verifica, falha o gate. Cada peculiaridade no site ao vivo (um H1 ausente, uma tag morta, um link quebrado) é reproduzida por padrão e ganha uma linha de decisão (id, tópico, escolha, padrão, motivo, custo se errado) dizendo isso e qual seria a correção.
Um registro tem um kind em vez de um field: { "kind": "redirect", "path": "<source>", "reason": "...", "action": "none", "approvedBy": "...", "date": "..." } significa "sem redirecionamento para este caminho, de propósito" (um redirecionamento ao vivo registrado para um href quebrado que nada deve redirecionar). Cada chave é obrigatória: ele remove um comportamento ao vivo, então é aprovado como qualquer exceção. O gate de redirecionamentos solicita o caminho e passa apenas quando as regras não o redirecionam; em um caminho que o gate não verifica, falha como caminho não verificado; verify-live o pula com o motivo. Todo outro gate o ignora.
5. Builds de staging, propostas e release
PUBLIC_INDEXABLE e PROPOSALS mudam o que o Astro constrói, então defina-os na build, não apenas na execução do gate; uma execução de gate em uma build feita sem eles verifica o site errado. Passe --origin apenas quando a própria build foi feita para aquele host; o site de uma build scaffolded é a origem ao vivo, então seus canônicos e sitemap permanecem nela.
npm run build && npm run verify -- --endpoint ... --thank-you ... # staging
PROPOSALS=on npm run build && npm run verify:proposals -- --endpoint ... --thank-you ... # propostas
PUBLIC_INDEXABLE=true npm run build && npm run verify:release -- --endpoint ... --thank-you ... # release
Após um deploy de staging, verifique-o via HTTP contra a baseline. Um deploy de release é verificado da mesma forma sem --expect-noindex:
npm run verify:live -- --base https://my-site.vercel.app --expect-noindex
node <skill-dir>/scripts/verify-live.mjs --base https://my-site.vercel.app --expect-noindex # fallback
--bypass "$VERCEL_AUTOMATION_BYPASS_SECRET" aplica-se apenas quando o staging foi deliberadamente protegido (o valor de bypass do ambiente, nunca colado no chat); um alias de staging público não precisa de nenhum.
Ele verifica cada rota da baseline em 200 (ou uma --sample), cada redirecionamento em um hop com o mesmo tipo de status, noindex em páginas se e somente se --expect-noindex, o sitemap, e 20 arquivos de mídia por bytes (ou --media-manifest, cujo url pode ser a URL absoluta da loja ou um caminho do site verificado em <base><path>). Um redirecionamento listado em redirects-skipped.json ou coberto por uma exceção "sem redirecionamento" (--skipped, --exceptions; ambos lidos da raiz do site por padrão) é pulado e nomeado com seu motivo. Cada requisição expira após 30 s, uma falha dessa verificação em vez de um bloqueio. Saída 3 significa que foi bloqueado: espere e reexecute. Saída 3 com deployment protection: significa que o Vercel respondeu com seu redirecionamento de login (302 para vercel.com/sso-api): use o alias de produção do projeto (público por padrão) em vez da URL de deploy bruta; apenas quando o staging foi deliberadamente protegido e você tem o segredo de bypass você pode contorná-lo com --bypass.
6. Aplicações de habilidades adjacentes
convert-site mira em quem está migrando (WordPress → Astro, ou de um SSG para Astro); astro-conversion-kit mira em quem já migrou e precisa provar que a nova build é correta. O kit é executado durante o desenvolvimento e em CI, garantindo que cada alteração no código não quebre nenhuma das características do site ao vivo original. Ele é ideal para migrações onde a precisão total é necessária, como sites governamentais, de comércio eletrônico ou com muitos redirecionamentos complexos.