パリティゲート
capture/ からベースラインを一度だけ構築し、すべてのゲートが通過するまで、すべてのビルドをそれに対してゲートします。ゲートは baseline/ のみを読み取り、ライブサイトにはアクセスしないため、古いホストがなくなった後でもビルドを検証できます。
黄金律
デフォルトのビルドはライブと等しく、ゲートは決して緩められません。 失敗を警告に変えるフラグはありません。これに従うさらに4つのルール:
baseline/を手動で編集しないでください。 唯一の例外はbaseline/scripts.jsonのkeepで、これによってベンダーを削除します(falseに設定し、その決定を記録します)。- 差分は編集ではなくエントリです。 回避不能なものは
exceptions.jsonに。改善案はproposals.jsonに、PROPOSALS=onの場合のみ有効になります。両方ともreferences/exceptions.mdに記載されています。 - スキップは合格ではありません。 何かをチェックできなかったゲートは、サマリー行に
N skipped (reason)と表示します。すべてのスキップを報告します。 - 誤ったゲートはキットのバグとして報告し、サイト内のゲートやベースラインは決して変更しないでください。
スクリプトの実行
convert-site によって作成されたサイトには scripts/ にスクリプトがあります。サイトルートから以下の npm スクリプトとして実行し、オプションは -- の後に指定します。スキャフォールドされたサイト以外では、各ステップの下に示された node 形式をサイトルートから実行します。ここで <skill-dir> は、npm install が実行されたキットのコピー内にあるこのスキルのフォルダの絶対パスです。すべてのスクリプトは --help を出力します。ソースを読む代わりにそれを実行してください。終了コード0は合格、1は失敗したゲートまたはドリフト、2は使用方法または入力不足、3はブロック(待機して再実行)を示します。
1. ベースライン(一度だけ)
npm run baseline
node <skill-dir>/scripts/build-baseline.mjs --capture capture --out baseline # フォールバック
これには capture/inventory.json、media.json(キャプチャの media.mjs)、および templates.json(templates.mjs)が必要です。いずれかが欠けている場合は、実行するステップを指定して終了コード2で終了します。baseline/{routes,seo,redirects,media,forms,scripts,meta,counts}.json、baseline/text/、およびサイトルートに redirects.csv を書き込みます。ターゲットのないショートリンクは除外され、stderr でその名前が報告されます。キャプチャされたチェーンの各中間ホップは、最終ターゲットへの直接の行として独自の行を取得します(/old-services → /old-services/ → /services/ は /old-services/,/services/ も生成します。hop of /old-services に注意)。ただし、そのホップ自体が構築されたルートである場合は除きます。これにより、チェーンの任意の URL へのリンクが1ホップで着地します。
baseline/ と redirects.csv を一緒にコミットします。--check は baseline/ を counts.json に対して再カウントします(ドリフトの場合は終了コード1)。すべてのファイルの形状は references/contracts.md にあります。
2. リダイレクト
redirects.csv(from,to,status,source,note)は、サイトのリダイレクトの単一の情報源です。source はキャプチャからの crawl、sitemap、shortlink、または seed です。追加する行は decision で、その note は決定IDを指定します。コンパイルされた出力を手動で編集しないでください。コンパイル:
npm run redirects:build
node <skill-dir>/scripts/build-redirects.mjs --csv redirects.csv --host vercel --out vercel.json --trailing-slash always # フォールバック
--host cloudflare --out public/_redirects(Pages の場合。Astro はそれを dist/ にコピーします)。Vercel:redirects キーは置き換えられ、他のすべてのキーは保持され、trailingSlash は削除され、サイトのルールの後にコンパイラ自身の308スラッシュルールとして書き込まれるため、スラッシュのないソースでも1ホップになります。静的ファイル拡張子で終わるパスのみが、これらのルールのファイルとしてカウントされます(大文字小文字を問わず):Webタイプ、ドキュメント、メディア、アーカイブタイプ(アップロードフォルダが保持するもの:docx、xlsx、csv、mov、wav、heic、gz、...)、および dist/(そうでない場合は public/)で見つかったすべての拡張子。そのため、ビルド後に再実行してください。/upgrade-guide/3.5/ はページであり、スラッシュを保持します。ルールがリダイレクトするであろう構築済みファイルは、built file redirected としてリダイレクトゲートで失敗します。
Cloudflare:クエリ行(/?p=7)は _redirects で一致させることができず、cloudflare-query-redirects.csv に送られ、シングルリダイレクトとして作成されます(「あなたが必要」な項目)。ターゲットが自身のパスであるクエリ行(/?p=2 から /)は何もせず、両方のホストで除外されます。
キャプチャされた行(decision 以外の source)はライブURLであり、パラメータを保持しません。Vercel の場合、その中のすべての : と * はエスケープされます(/foo:bar/ は /foo\:bar/ と書き込まれ、キャプチャがたどった壊れたライブ href である /trackshttps://www.example.com/x/ も同様です)。// は保持されます。decision 行のみが :name パラメータと * スプラットを使用し、そこでも数字の前の :(ポート)はエスケープされます。Vercel はベアの : を拒否し、ゲートのシミュレーションも同様です。有効な形式がないソース(先頭の / なし、空白、制御文字。Cloudflare ではキャプチャされた :<name> または * も含み、_redirects はこれらをエスケープできません)はドロップされ、stderr で名前が報告され、redirects-skipped.json に { source, reason } としてリストされます。そのファイルは redirects.csv の隣(サイトルート)にあり、決して public/ の下にはないため、デプロイされません。リダイレクトゲートと verify-live は、リストされた行をスキップとしてカウントし、合格とはカウントしません。パスに単にリダイレクトがないようにする必要がある場合は、代わりに「リダイレクトなし」例外を記録します(セクション4):{ "kind": "redirect", "path": "<source>", "reason": "...", "action": "none", "approvedBy": "...", "date": "..." }。
3. 検証
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/ # フォールバック
run-all は10個のゲートをアルファベット順に実行し、各ゲートに受け入れるオプションのみを渡し、最初の失敗で停止します。共有オプション:--paths(カンマ区切りリスト、* は任意のものに一致)、--json、--origin(リンク、SEO、サイトマップ:ビルドの対象ホスト)、--allow-extra(ルート)、--endpoint、--honeypot、--thank-you(フォーム。ベースラインにPOSTフォームがある場合、--endpoint は必須)、--host、--config(リダイレクト、リンク)、--csv(リダイレクト)、--media-plan(メディア)。最初の失敗を修正し、再ビルドし、再実行します。単一のゲート:node <skill-dir>/scripts/verify/<gate>.mjs。
| ゲート | 合格条件 | スキップの意味 |
|---|---|---|
forms | 各ライブフォームがページ上にあり、同じフィールド名が順序とメソッドで存在する。POSTフォームのアクションは --endpoint であり、--honeypot を運ぶ。GETフォーム(検索ボックス)はライブのアクションパスを保持し、どちらも必要としない(「N個の検索/GETフォームを比較」)。ライブアクションが別の登録ドメイン上にあるPOSTフォーム(ニュースレターリストホスト。www、http、サブドメイン、および admin-ajax.php や ?wpcf7 などのWordPressハンドラーはサイトとしてカウントされる)はそのまま保持され、完全なアクションURLによってGETフォームと同様に比較され、--endpoint を必要としない(「N個のフォームはそのまま保持(にPOST)」) | --thank-you がない:POSTフォームのサンキューページがチェックされなかった |
headers | ホスト設定が、すべてのパスで nosniff、Referrer-Policy、X-Frame-Options または CSP frame-ancestors を送信し、インデックス可能でない場合は、すべてのパスで X-Robots-Tag: noindex を、または(Vercel)すべての構築済みページでメタロボット noindex を送信する。プレビューホストルール(https://*.pages.dev/*)はプレビューホストに対してのみカウントされる。PUBLIC_INDEXABLE=true の場合、すべてのパスでの noindex は禁止される | |
links | すべての内部 href と src が、構築済みのページまたはファイル、リダイレクトソース、ベースラインメディアのいずれかに解決されるか、コンパイル済みルール(vercel.json または dist/_redirects:スラッシュルールとすべての redirects.csv 行)を介して1ホップで構築済みページに着地する。ライブがリダイレクトしたのと同様(「N個のリンクがリダイレクトを通じて解決」)。2ホップまたは欠落したターゲットは失敗。ホスト設定がない場合、ビルドが /about/ を提供する /about は失敗 | |
media | すべてのベースラインメディアファイルが、同じバイトでビルドによって提供される(--paths は、そのパスまたはそれを参照するページによってファイルを選択する)。dist/ 内(プランモード repo)。プランの prefix 下、ストアマニフェスト(store)内、または引き続きライブ上(live-temporary)。プレフィックス外、すべてのモードで dist/ 内。PUBLIC_INDEXABLE=true の場合、live-temporary はプレフィックス下のすべてのファイルで失敗する(リリースビルドはライブサイトからメディアを提供してはならない。プレフィックスなしのプランはすべてのファイルをカバーする)が、プレフィックス外の dist/ 内のファイルは引き続きチェックする | プランモード live-temporary のステージングビルド:プレフィックス下のファイルはライブから提供されるため、チェックされない |
redirects | すべてのベースラインリダイレクトと csv 行が、同じステータスで1ホップで応答し、ターゲットが dist/ 内で200で応答する(または外部であるか、ベースライン meta.json の excluded のプレフィックス下にある場合:最初のホップのみがチェックされる)。ソースが構築済みファイルでもない。構築済みページまたはファイルが、自身のURLからリダイレクトされない(built page redirected by rule <rule>。例外でカバーされない場合はルールを修正)。「リダイレクトなし」例外(kind: "redirect"、action: "none")のあるパスは、まったくリダイレクトされない | 何もしないクエリ行、Cloudflare 上の任意のクエリ行、または有効な形式がなく redirects-skipped.json に理由(not compiled)とともにリストされているソース |
routes | すべてのページ、投稿、アーカイブ、フィードが200で構築され、noindex ルートは still noindex を言い、余分なものは構築されない(baseline/js.json 内のキャプチャ自身のスクリプトと baseline/css.json 内のスタイルシートがライブパスにあり、baseline/sitemaps.json 内のライブサイトマップ名、および起動アイコンセットは余分ではない)。200、3xx、404、410以外のステータスでキャプチャされたルートは、routes 例外がそれをスキップするか、その構築済みの代替を指定しない限り、not captured: <status> として失敗する | to: "skip" を持つ routes 例外:そのルートはキャプチャされなかった |
scripts | 保持されている各ベンダーは、正確にそのライブページ上に同じIDで存在する。削除されたものはどのページにも存在しない。新しいベンダーはいない | |
seo | タイトル、説明、canonical、robots、lang、h1、h2、og、JSON-LD @types がページごとに等しい。サイトURLはサイトのオリジン上にある。PUBLIC_INDEXABLE=true でない限り、robots はステージングの noindex, nofollow を運ばなければならず、残りはこれら2つを超えてライブのものと等しくなければならない。リリースビルドは robots を正確に比較する | |
sitemaps | dist/ 内のすべてのトップサイトマップ(Astro の sitemap-index.xml、または baseline/sitemaps.json 内のライブ名(wp-sitemap.xml や sitemap_index.xml など))が、オリジン上のインデックス可能な200ページを正確にリストする(パーセントエスケープは大文字小文字を区別せず比較:%c3 は %C3)。すべての robots.txt Sitemap: 行はオリジン上にあり、dist/ 内のファイルを指定するか、ライブ robots.txt(capture/robots.txt)がそのまま運んだ行である。すべてのフィードは、同じ数のアイテムで構築されている | |
text | 各ページの表示テキストが baseline/text/ と等しい(ワード差分、5ワードのコンテキスト) |
--exclude を使用して作成されたキャプチャは、そのパスプレフィックスを baseline/meta.json の excluded に記録します。links ゲートは、その下のリンクをチェックしません(別のホストへのリンクと同様、「N個のリンクが除外パス下にあるためチェックされません」と注記されます)。sitemaps ゲートは、ライブ robots.txt がそのままその行を運んでいた場合、その下の robots.txt Sitemap: 行を受け入れます(同じホスト上のマルチサイトサブサイトのサイトマップ)。
4. 差分と決定
ビルドがライブと一致できない、または一致すべきでない場合は、references/exceptions.md を読んでください。簡単に言うと、エントリは { path, field, from, to, reason, approvedBy, date } です。from: "" はビルドが追加する値を記録し、to: "" はビルドが削除する値を記録します。一致しなくなった例外、またはゲートがチェックしないパスを指定する例外は、ゲートを失敗させます。ライブサイトのすべての異常(欠落した H1、デッドタグ、壊れたリンク)はデフォルトで再現され、それを述べ、修正方法を示す決定行(id、topic、choice、default、reason、cost if wrong)を取得します。
1つのレコードは、field の代わりに kind を持ちます:{ "kind": "redirect", "path": "<source>", "reason": "...", "action": "none", "approvedBy": "...", "date": "..." } は「このパスにはリダイレクトなし、意図的に」を意味します(何もリダイレクトすべきでない壊れた href に対して記録されたライブリダイレクト)。すべてのキーは必須です:これはライブの動作を削除するため、他の例外と同様に署名される必要があります。リダイレクトゲートはパスを要求し、ルールがそれをリダイレクトしない場合にのみ合格します。ゲートがチェックしないパス上では、path not checked として失敗します。verify-live は理由とともにそれをスキップします。他のすべてのゲートはそれを無視します。
5. ステージング、プロポーザル、リリースビルド
PUBLIC_INDEXABLE と PROPOSALS は Astro がビルドするものを変更するため、ゲートの実行だけでなくビルドに設定してください。これらなしで作成されたビルドに対してゲートを実行すると、間違ったサイトがチェックされます。--origin は、ビルド自体がそのホスト用に作成された場合にのみ渡します。スキャフォールドされたビルドの site はライブオリジンであるため、canonical とサイトマップはその上に留まります。
npm run build && npm run verify -- --endpoint ... --thank-you ... # ステージング
PROPOSALS=on npm run build && npm run verify:proposals -- --endpoint ... --thank-you ... # プロポーザル
PUBLIC_INDEXABLE=true npm run build && npm run verify:release -- --endpoint ... --thank-you ... # リリース
ステージングデプロイ後、HTTP経由でベースラインに対してチェックします。リリースデプロイは、--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 # フォールバック
--bypass "$VERCEL_AUTOMATION_BYPASS_SECRET" は、ステージングが意図的に保護されている場合にのみ適用されます(環境からのバイパス値。チャットに貼り付けないでください)。パブリックなステージングエイリアスは必要ありません。
これは、すべてのベースラインルートを200でチェックし(または --sample)、すべてのリダイレクトを同じ種類のステータスで1ホップでチェックし、--expect-noindex の場合にのみページの noindex をチェックし、サイトマップ、およびバイト単位で20個のメディアファイル(または --media-manifest。その url はストアの絶対URLまたは <base><path> でチェックされるサイトパスの場合があります)をチェックします。redirects-skipped.json にリストされている、または「リダイレクトなし」例外(--skipped、--exceptions。どちらもデフォルトでサイトルートから読み取られます)でカバーされているリダイレクトはスキップされ、その理由とともに名前が付けられます。各リクエストは30秒後にタイムアウトします。これはブロックではなく、そのチェックの失敗です。終了コード3はブロックされたことを意味します:待機して再実行してください。終了コード3と deployment protection: は、Vercel がログインリダイレクト(302 to vercel.com/sso-api)で応答したことを意味します。未加工のデプロイメントURLではなく、プロジェクトの本番エイリアス(デフォルトでパブリック)を使用してください。ステージングが意図的に保護されている場合のみです。