mcpField
V
visual-qa

visual-qa

github.com/rheinmirテスト・品質1079 回インストール2 週間前に更新
リポジトリ

visual-qa について

visual-qaは、ローカルSPAを確認するためのヘッドレス視覚テストワークフローです。スクリーンショットのベースライン比較、ページ内のデザイン・アクセシビリティ検査、全ルートの確認を組み合わせ、修正後に再検証します。

主な機能

  1. 01ルートのスクリーンショットとマニフェストを生成
  2. 02ピクセル単位でベースラインと比較
  3. 03ページ内のデザインとアクセシビリティを検査
  4. 04全ルートの問題を確認
  5. 05修正後に再撮影して検証

活用シーン

  1. 01再デザイン後の意図しない視覚変化の検出
  2. 02アプリの各テーマや状態でのUI変更確認
  3. 03ブラウザー拡張でlocalhostを撮影できない場合のヘッドレス検査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/rheinmir/setup --skill visual-qa

インストール後、Claude Code で/visual-qaを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

assets/
.gitignore
package-lock.json
package.json
route-shots.mjs

詳細

スキル:visual-qa — 視覚的セルフチェックのワークフロー(capture → diff → judge → fix → re-verify)

存在理由: UI を修正したエージェントが 自画自賛 するのは、体系的な失敗です(failure ui-pass-without-full-visual-review)。スクリーンショットを撮っても、誰も開かなければテストしていないのと同じです。このワークフローは、「エージェントが自分で見て、自分で合格とする」方法を、3 層の独立したチェックに置き換えます。そのうち 2 層は機械によるものです。

2026/07/13 の判断(/last30days より): AI 生成 UI に関して業界で成熟しているものは、ただ 1 つです。それは ベースラインスナップショット + ピクセル差分です(argos-ci/argos:「意図しない視覚的な変更を検出」;Playwright toHaveScreenshot)。「LLM がルーブリックに基づいてスクリーンショットを評価する」層はまだ最先端の段階です(Agent-as-a-Judge、論文段階)。そのため、誰かが対応するのを待たずに、この層は自分たちで実装します。

WHAT

目的とコンテキスト
  • 目的: SPA のローカル環境で、「エージェントが自分で見て、自分で合格とする」方法を、3 層の独立したチェックに置き換えます。ベースラインのピクセル差分(機械)、デザインと a11y の不変条件(機械)、ルーブリックに沿った全画像の確認(エージェント)を行い、機械と目視の両方で問題がない場合に限り、証拠となる画像ファイルを添えて合格とします。
  • トリガー(使用するタイミング):
    • テーマ/UI/ブランドを変更し、意図せず何かを壊していないか確認する必要がある場合。
    • ui_role≠none のフレームで受け入れ判定のゲートが必要な場合(スキル br の「視覚的セルフチェックのループ」を参照)。
    • ブラウザー拡張機能で localhost をキャプチャできない場合(よくあります)→ 代わりに headless CLI を使用します。
    • ユーザーが「視覚 QA」「visual qa」「route-shots」「各 route のスクリーンショット」「UI をチェック」「ベースライン差分」「UI にリグレッションがあるか」「headless で UI テスト」「/visual-qa」と言った場合。
  • 対象外: computer-use(デスクトップ AX/Orca ブラウザー)と重複しません。4 項目のシニア判定ではありません(それは /qc-uiux で、このエンジンを使用します)。「エラーをなくす」ために閾値を緩めません。
メンタルモデル

実行中のアプリ + 実際の route リスト + ログイン → route-shots(画像 + MANIFEST、すべての theme/state)→ 第 1 層 ベースライン差分(閾値 0)→ 第 2 層 ページ内不変条件(contrast-aa · monochrome-surface · rogue-slab · shadow-clipped · overlap · row-misalign)→ 第 3 層 N 枚すべての画像をルーブリックに沿って確認 → FINDINGS.md → 修正 → 再撮影 + 再確認。

入力と出力の契約
フィールド必須?意味
In実行中のアプリ(--base)はいlocalhost の SPA
In実際の router から取得した route リスト + ログインアカウントはい推測しない;ログインに失敗した場合はゲートを失敗させる必要があります
Inアプリ自体から取得した theme/state の一覧はいアプリに 2 種類より多くある場合に light,dark をハードコードしないこと
Inベースラインディレクトリいいえ初回に自動作成します
Out<out>/*.png + MANIFEST.jsonはいファイルがなければ = 一度も撮影していません
Out--assert 使用時の終了コードゲートでは必須route エラー/画像不足/不変条件違反/ベースラインとの差異がある場合は 1
OutFINDINGS.mdはい「問題なし」を含め、route ごとに必ず 1 行
ルールと機能

血のにじむ教訓 — 繰り返さないでください:

  • RULE-01 (MUST): 計算できるものは必ず機械に計算させてください — 目視に任せるのは禁止です。 文字のコントラストは 数値 です(WCAG ratio = f(文字色, 背景色))。ルーブリックに「文字がAAに適合」と書いてあっても、不変条件を設定しなければ評価器は盲目です。薄い文字がログインページ全体をすり抜け、エージェントも画像を見ただけでは判別できません。定量化できる基準は、必ずassertにしてください。ルーブリックの文章にしてはいけません。
  • RULE-02 (MUST): どのルートも盲点にしてはいけません。 ドライバーが先にログインすると ⇒ /auth にリダイレクトされ ⇒ サインインページは一度も監査されません — まさに不具合があるページなのに。まず未ログイン状態を撮影し、その後にログインしてください。再確認してください。モーダル、エラー、空、読み込み中など、まだ一度も画面に収めていない状態はありませんか?
  • RULE-03 (MUST): テーマも状態のひとつです — lightとdarkの両方を撮影してください。 アプリは通常、prefers-color-scheme ではなく アプリの設定 に応じてクラス(<html> の .dark)を有効にします ⇒ headlessでは、デフォルトでlightしか見えず、darkは完全な盲点になります。ユーザーがdarkでアプリを開くとUIが壊れているのに、ゲートは合格してしまいます。--themes light,dark(デフォルト)を有効にすると、初回実行で、lightは問題ない一方、darkではAAAに不適合な箇所が172か所見つかりました。設計上の帰結:アクセントカラーはテーマに応じて切り替える必要があります(明るい背景 → 暗いアクセント、暗い背景 → 明るいアクセント)。両方のテーマで同じアクセントカラーを使うのは、定義上誤りです。また、アクセント上の文字色をハードコードするのも禁止です(color:#fff)。もう一方のテーマでは白地に白になり、コントラスト比は1.13:1です。
  • RULE-04 (MUST): アプリが提供するすべてのテーマを網羅してください — リストはアプリから取得し、独自に決めないでください。 アプリにはテーマが3つ(default / default-dark / paper)あるのに、light,dark をハードコードしてしまいました ⇒ paper は誰も確認せず ⇒ darkと同じように壊れており、ゲートではなくユーザーが見つけました。ルール:実行前に、アプリ自体からテーマ一覧を読み取ってください(Settings→Preferencesのドロップダウン、またはソースの THEME_OPTIONS)。それからすべてを網羅します。一般化すると、状態のあらゆる軸(テーマ、ロケール、ロール、データなし/全データ)は、エージェントの記憶ではなくアプリから取得する必要があります。
  • RULE-05 (MUST): 状態の変更は、アプリが変更するときと同じ経路で行い、実際に変わったことを検証してください。 立て続けに2段階の失敗がありました。① .dark クラスを手動で付与した(アプリは data-theme とCSSの注入を使用しているため、その仕組みは存在しない)。② localStorage を設定した(アプリがサーバー上のアカウント設定で上書きする)。どちらの場合も、実在しない状態でゲートが合格しました — 不合格よりも悪い結果です。確実に正しい方法は、ユーザーがクリックする経路です(Settings → Preferences → 選択)。そして必ず不変条件を設けてください。異なる2つのテーマで背景がまったく同じなら、画像は偽物です ⇒ ASSERT FAIL(ゲートは同じテーマを2回撮影して、自分を褒めているだけです)。
  • RULE-06 (MUST): テーマはアプリの仕組みです。まずソースを読んでください。 .dark や prefers-color-scheme を前提にしないでください。documentElement.classList / data-theme / localStorage / <style> の注入をgrepして、アプリが何でテーマを変更するかを確認してください。テーマは通常、実行時にCSSを注入して読み込まれます ⇒ 通常のトークンでは静的なテーマファイルより優先されますが、!important ルールには負けます ⇒ UIが中途半端になります(アプリのテーマの背景 + 自分のテーマのカード = 画面の中に奇妙な色の領域)。正しい修正方法:独自の配色は、アプリが注入するテーマファイルそのものに記述してください。アプリが決して読み込まない場所に .dark{} ブロックを書いてはいけません。
  • RULE-07 (MUST): ログインが失敗したら、ゲートも失敗しなければなりません。 ログインに失敗すると、すべてのルートが /auth にリダイレクトされます ⇒ ドライバーはサインインページのコピーを8枚監査し、「design-ok」と出力します。このような合格は不合格よりも悪い結果です。一度も見たことのないアプリを確認済みだと証明してしまいます。ログインに失敗したら --assert が直ちに失敗するようにしてください。
  • RULE-08 (MUST): ユーザーが不具合を報告し、機械が問題なしと判定したら、間違っているのは機械であり、ユーザーではありません。(2026年7月14日の教訓 — ユーザーの言葉:"これは開発であって裁判ではないのに、自分の主張をかたくなに守るんだね"。)ゲートの合格は盾にはなりません。証明できるのは、教えられたことを、撮影できた状態で確認したことだけです。ユーザーが不具合を見たなら、ゲートが一度も確認していない状態/テーマ/バンドルが存在します。推測でユーザーに反論するのは禁止です(「たぶんキャッシュのせい」「たぶんキーを押し間違えた」)。推測するたびに検証作業をユーザーに押しつけることになり、私はそのやり方で3回連続で間違えました。正しい手順は、(1) 報告を信じる、(2) ユーザーとまったく同じ状態(ルート + テーマ + バンドル + ログイン/フォーカス状態)を再現する、(3) 測定してから話す、です。再現できなければ、「まだ再現できていません」と正直に伝えてください。仮説を作り上げて、それを擁護してはいけません。
  • RULE-09 (MUST): 撮影前にランダム要因を取り除き、しきい値を緩めないでください。 点滅するカーソルとアニメーションによって、すべてのルートのベースラインが撮影のたびに約0.04%ずれます ⇒ ゲートはいつまでも不合格 ⇒ 無視される ⇒ 本当のリグレッションが見逃されます。ドライバーの初期化時にCSSで animation/transition:none; caret-color:transparent を注入し、CodeMirrorのカーソルも非表示にすると、差分は**0%**になります。--diff-threshold を緩めて「警告を止める」のは、機械を盲目にする行為であり、バグの修正ではありません。
  • RULE-10 (MUST): 欲張りなセレクターは自分の首を絞めます。 ノイズ除去用CSSの [class*="cursor"] は、Tailwindの cursor-pointer/cursor-text にも一致します ⇒ 入力欄が非表示になり、ログインに失敗し、ゲートが検査対象のアプリを壊します。ゲートが注入するCSS/セレクターはすべて、可能な限り狭くしてください。追加後は必ずフルフロー(ログイン → すべてのルート)を実行して確認してください。
  • RULE-11 (MUST): ゲートはクリーンなリポジトリから実行できなければなりません。 playwright-core がないと ⇒ node route-shots.mjs はimport行で直ちに停止します ⇒ UIフレームのacceptance_testが一度も実行されません = 誰にも気づかれずにゲートが停止します。依存関係は skills/visual-qa/assets/package.json に記載します。インストール:npm i --prefix skills/visual-qa/assets。
  • RULE-12 (MUST): 言葉より証拠。 pass/fixedが有効なのは、画像ファイルがある場合だけです。ファイルがなければ、まだ実施していません。推測によるpassの報告は禁止です。
  • RULE-13 (MUST): 選り好みせず、すべて読んでください。 画像を7枚中3枚だけ読んで結論を出すのは、ui-pass-without-full-visual-review failureの根本原因です。未確認の画像に原因が最初から写っています。
  • RULE-14 (MUST): 「機械はOK」≠「UIが美しい」。 不変条件がわかるのは、教えられたことだけです。ゲートの合格を、画像を読む手順を省く言い訳にしてはいけません。
  • RULE-15 (MUST): しきい値は諸刃の剣です。 しきい値が緩すぎると本物の不具合を見逃します(差分0.5%で、カード全体を平らにするリグレッションが見逃されました)。厳しすぎると誤検知になり、ノイズの多いゲートは停止します。ベースラインにはしきい値0と手動レビューを使ってください。DOMの不変条件には、本物の不具合とノイズを区別できるしきい値が必要です。
  • RULE-16 (MUST): CSSは推測せず、測定してください。 目視での診断は4回連続で間違えました(クリップ → 衝突 → セレクターの誤り → specificityの誤り)。getComputedStyle を照会して、初めて根本原因がわかりました。CSSを修正する前に、必ず computed style と rect を測定してください。その後で修正します。修正後は、画像を見る前にもう一度測定してください。
  • RULE-17 (MUST): 「エラーをなくす」ために機械を盲目にしないでください。 blurを検出器のしきい値より下げるのは、不具合の隠蔽であって修正ではありません(オーラは依然として切り取られます)。測定器ではなく、欠陥を修正してください。
  • RULE-18 (MUST): overflow:hidden のコンテナ(狭いrail、タブバー)では、outsetシャドウを使わないでください。 クリップされたり、すき間に落ち込んだりして、見苦しい筋ができます。inset(はみ出さない)を使うか、シャドウをなくしてください。もう一方の軸が auto の場合、overflow-x:visible は実現できません(CSSによって auto に強制されます)。
  • RULE-19 (MUST): playwright-core + システムのChromeを使用してください(Chromiumを130MBもダウンロードしない)。ベースライン差分には pixelmatch + pngjs が必要です(小さいパッケージです。ない場合はベースライン検査を自動的にスキップします)。
  • RULE-20 (MUST): リアルタイムSPAで networkidle を使うのは禁止です — domcontentloaded + 固定時間の待機を使ってください。
  • RULE-21 (MUST): computer-use と競合させないでください(デスクトップAX/Orcaブラウザー)。このスキルは、localhostのSPA向けheadless CLIです。
  • 機能:localhost上でブラウザーをheadless実行する、ページ内でcomputed style + rectを測定する、ピクセルをベースラインと比較する、画像/manifest/findingsを記録する、対象プロジェクトのUIを修正する。
失敗の境界
  • ログイン失敗 → --assert は即座に failed。サインインページのコピーは監査しません。
  • playwright-core がない → import 行でゲートが停止します:blocked。npm i --prefix skills/visual-qa/assets をインストールしてください。
  • ベースラインとの差分が 0 ではない → 閲覧し、--update-baseline で承認するまで failed。
  • ユーザーからエラーの報告があったのにマシンが問題なしと判定 → マシンの誤りです。同じ状態を再現してください。再現できない場合は「再現できませんでした」と明言してください。
  • マシンの判定は問題なしでも、N 枚の画像をすべて読んでいない → 未完了。

方法

主なワークフロー

W01~W07 は、以下のステップ 0~6 に対応します。

ステップ種別入力アクション出力/終了条件失敗時/次の手順
W01決定的アプリ、ルーター、アカウントアプリの稼働を確認し、アプリから実際のルート一覧とテーマ一覧を取得しますルート一覧+テーマ一覧アプリが動作しない → blocked
W02副作用ありルート一覧route-shots.mjs(+--assert)で撮影します画像+MANIFEST.jsonログイン失敗 → failed
W03決定的画像+ベースライン閾値 0 でベースライン差分を確認します差分0 ではない → 確認して承認(B02)または修正
W04決定的ページ内マシン検証可能な不変条件違反または問題なしハード FAIL → W06
W05判断MANIFEST の N ルートRead で N 枚すべての画像を読み、ルーブリックに沿って評価しますルートごとに 1 行未読画像がある → 未完了
W06副作用あり指摘事項FINDINGS.md(根本原因)を作成 → 修正修正済みコード—
W07決定的修正済みコード再撮影 → 再読マシン判定も目視判定も問題なし問題が残る → W06

各ステップの詳細(W01~W07 の信頼できる情報源):

0. アプリが稼働していること。 ルート一覧(推測せず、実際のルーターから取得)とログインアカウントを把握してください。

1. 撮影+証拠。

node skills/visual-qa/assets/route-shots.mjs \
  --base http://localhost:5230 --out <dir>/qa-shots \
  --baseline <dir>/qa-baseline \
  --user demo --pass demo --assert

<out>/*.png + MANIFEST.json(ルート → ファイル → バイト数)を生成します。ファイルがない場合は、一度も撮影されていません。

2. ベースライン差分(マシン第 1 層)—意図しない変更を検出します。 初回はベースラインを自動作成します。次回以降はピクセル単位で比較します。デフォルトの閾値 = 0:差分が 0 ではないものはすべて FAIL とし、--update-baseline を使って確認し、手動で承認する必要があります。これは、CSS 不変条件の検査でもエージェントの目でも検出できない種類の問題(例:過剰な修正で UI 全体を平坦化してしまう)を検出できる唯一の層です。

3. マシン検証可能な不変条件(マシン第 2 層)—デザイン準拠+アクセシビリティ。 computed style を使ってページ内で実行します:

  • contrast-aa — すべてのテキストノードで WCAG AAA(文字色+実効背景色からコントラスト比を算出。通常の文字は 7:1、大きな文字は 4.5:1)。ハード FAIL。
  • monochrome-surface — 大きなペイン(サイドバー/aside/nav)の背景色は統一されている必要があります。ハード FAIL。
  • rogue-slab — すべてのビューポートの 2% 以上を占める要素について、不透明な背景色が body の背景色から 1.6:1 を超えて異なる場合に検出します。ハード FAIL。 (monochrome-surface が調べるのはサイドバー/aside/nav のみのため、ページ中央にある任意の <div> の不自然な色面は検出できません。)
  • shadow-clipped — overflow:hidden の親要素内で、外側に広がるオーラが切り取られている場合(ぼかしの閾値あり)。警告。
  • overlap — 親子関係にない 2 つのコントロール(button/a/input/[role=button])の矩形が、縦横両方向で 4px 以上重なっている場合に検出します。ボタン同士の重なり(例:バッジ幅が変動するのに、position:fixed とマジックオフセットを使用)を検出します。ハード FAIL。
  • row-misalign — 同じ親要素内のコントロールがほぼ同じ行(top のずれが ≤6px)にあり、高さの差が >3px の場合に検出します。隣り合うボタンの高さが揃っていない問題(例:パディングやフォントが異なり、トグルボタンがバッジより高い)を検出します。ハード FAIL。 (この 2 つのルールは 15/07/26 に追加されました。/redesign-existing-projects(CSS を静的に読み取るため、矩形の測定はできません)も unit-test(コンテンツを確認するだけで、座標は確認しません)も、レンダリング時の幾何学的な問題を検出できないためです。overlap の閾値に面積比を意図的に使わないのは、実際のバグではボタンの端だけがクリップされる(面積比 18%)ことがあり、面積比の閾値では見逃すためです。独立した 2 つのコントロールが数 px でも重なっていれば問題です。)

プロジェクトのデザイン仕様に合わせて不変条件を拡張してください。

4. 画像を読む — 厳格な網羅性+ルーブリック(人/エージェント層)。 MANIFEST に N ルートがある場合、N 枚すべての画像を Read する必要があります。FINDINGS には、各ルートにつき 1 行(「問題なし」の場合も含む)を記載してください。主観ではなくルーブリックに沿って評価します。例:ニューモーフィズムの場合、① 単色の面 ② 影が切り取られていない ③ CTA はアクセントカラーのフラットなデザイン ④ 文字/アイコンは AA ⑤ empty-state はブランドに準拠 ⑥ フォーカスリング。

5. FINDINGS.md → 修正。 # · route · 問題 · 重大度 · **根本原因** · 修正方法 の表。

6. 再撮影 → 再読 → その後に初めて合格。 マシンの判定も目視判定も問題がないこと。どちらかが欠けている場合は未完了です。

ブランチ
ID種類ガード動作スキップ/失敗再合流
B01conditional_requiredフレームゲートでは1つのルートのみ必要--route <r> を実行—W03
B02user_optionalベースラインとの差異は、確認済みの意図的な変更--update-baseline画像を確認していない → 承認不可W04
B03capability_optionalpixelmatch と pngjs があるベースライン差分を実行ない → ベースラインを自動でスキップW04
B04recoveryユーザーから不具合の報告があったが、ゲートは成功状態(ルート + テーマ + バンドル + ログイン/フォーカス)を正確に再現し、測定してから説明再現できない → 率直に伝えるW02
検証と停止条件

機械:--assert の終了コード(ルートの不具合/画像不足/不変条件/ベースライン)。目視:FINDINGS に MANIFEST の各ルートにつき1行ずつあること。両方に問題がなく、かつ画像ファイルがある場合のみ合格です。ファイルがない場合=未実施です。

例
  • 成功例: アプリのテーマを変更し /br を生成、THEME_OPTIONS の7ルート × 3テーマ → route-shots.mjs --assert の終了コードは0、ベースライン差分は0%、エージェントがすべての画像を Read で確認、FINDINGS に「問題なし」の7行 → 画像付きで合格。
  • 境界/失敗例: light は問題ないが dark を網羅していない → --themes light,dark を有効にすると AAA に違反する箇所が172件見つかる → --assert の終了コードは1。テーマに応じて変わるようアクセントカラーを修正して再撮影し、--diff-threshold は緩和しない。
参考 — ドライバーフラグ

--base --out --user --pass · --route <r>(1ルート、フレームゲート用)· --assert (終了コード1となる条件:ルートの不具合/画像不足/不変条件違反/ベースラインとの差異)· --baseline <dir> · --update-baseline(意図的な変更を承認)· --diff-threshold <%>(デフォルトは0)。

コメント