mcpField
V
visual-evidence

visual-evidence

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

visual-evidence について

visual-evidenceは、プルリクエストやレポートでUI変更を記録するためのスキルです。ヘッドレスPlaywright Chromiumでスクリーンショットや操作GIFを撮影し、埋め込み用にアップロードします。証拠の収集はベストエフォートで、パイプラインを停止させません。

主な機能

  1. 01ヘッドレスChromiumでの撮影
  2. 02操作GIFの録画
  3. 03evidence-upload経由のクラウドアップロード
  4. 04プルリクエストへの証拠埋め込み手順
  5. 05ベストエフォート、120秒の制限

活用シーン

  1. 01修正前後の画面比較
  2. 02新機能の主要フローの提示
  3. 03読み込みや入力検証の操作確認
  4. 04プルリクエストでの視覚的変更の記録

インストール

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

$npx skills add https://github.com/fellowship-dev/dogfooded-skills --skill visual-evidence

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

詳細

visual-evidence

Playwright のヘッドレス Chromium を使用して、ブラウザーのスクリーンショットや GIF 録画をキャプチャします。PR やレポートに埋め込むため、クラウドストレージにアップロードします。

使用するタイミング

  • バグ修正の実装後(修正前後の比較)
  • 機能の実装後(主要なフローのデモ)
  • PR レビュー中に視覚的な変更を確認するとき
  • 操作やタイミングに関するバグ(ダブルクリック、読み込み状態、競合状態)→ GIF 録画を使用します

使用しないタイミング

  • バックエンドのみの変更、設定/インフラ、テストのみ、CLI ツール、UI のない API
  • Playwright のインストールに失敗した場合は省略します — 視覚的な証拠の取得はベストエフォートであり、パイプラインを停止させてはいけません

前提条件

リモート環境に Playwright と Chromium をインストールします。

cd /tmp && npm install playwright 2>&1 | tail -3 && \
  npx playwright install-deps chromium 2>&1 | tail -3 && \
  npx playwright install chromium 2>&1 | tail -3 && \
  echo PW_READY

GIF 録画を行う場合は、ffmpeg もインストールします。

sudo apt-get update -qq && sudo apt-get install -y -qq ffmpeg 2>&1 | tail -1 && echo FFMPEG_READY

ワークフロー

1. スクリーンショットのキャプチャ

/tmp/screenshot.mjs を作成します。

import { chromium } from '/tmp/node_modules/playwright/index.mjs';
const [url, output] = process.argv.slice(2);
const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
console.log(`Navigating to ${url}...`);
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });
const title = await page.title();
console.log(`Page title: ${title}`);
await page.screenshot({ path: output, fullPage: true });
console.log(`Screenshot saved: ${output}`);
await browser.close();

使用方法:

node /tmp/screenshot.mjs http://localhost:3000 /tmp/evidence-home.png
node /tmp/screenshot.mjs http://localhost:3000/admin /tmp/evidence-admin.png
2. GIF 録画(操作の証拠)

/tmp/record.mjs を作成します。

import { chromium } from '/tmp/node_modules/playwright/index.mjs';
const [url, output, ...actions] = process.argv.slice(2);
const browser = await chromium.launch({ headless: true });
const context = await browser.newContext({
  viewport: { width: 1280, height: 720 },
  recordVideo: { dir: '/tmp/videos', size: { width: 1280, height: 720 } }
});
const page = await context.newPage();
console.log(`Navigating to ${url}...`);
await page.goto(url, { waitUntil: 'networkidle', timeout: 30000 });

// Execute actions passed as JSON array
if (actions[0]) {
  const steps = JSON.parse(actions[0]);
  for (const step of steps) {
    if (step.action === 'click') {
      console.log(`Clicking ${step.selector}...`);
      await page.click(step.selector);
    } else if (step.action === 'dblclick') {
      console.log(`Double-clicking ${step.selector}...`);
      await page.click(step.selector);
      await page.waitForTimeout(200);
      await page.click(step.selector);
    } else if (step.action === 'wait') {
      console.log(`Waiting ${step.ms}ms...`);
      await page.waitForTimeout(step.ms);
    } else if (step.action === 'fill') {
      console.log(`Filling ${step.selector}...`);
      await page.fill(step.selector, step.value);
    } else if (step.action === 'screenshot') {
      console.log(`Snapshot: ${step.label}`);
      await page.screenshot({ path: `/tmp/evidence-${step.label}.png` });
    }
  }
}

await page.waitForTimeout(500);
await page.close();
await context.close();
await browser.close();

// Convert to GIF
const { execSync } = await import('child_process');
const webm = execSync('ls -t /tmp/videos/*.webm | head -1').toString().trim();
console.log(`Video: ${webm}`);
execSync(`ffmpeg -y -i "${webm}" -vf "fps=10,scale=1280:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" -loop 0 "${output}" 2>&1 | tail -3`);
console.log(`GIF saved: ${output}`);

使用方法:

node /tmp/record.mjs http://localhost:3000/form /tmp/evidence-before.gif \
  '[{"action":"click","selector":".submit-btn"},{"action":"wait","ms":2000}]'
3. クラウドストレージへのアップロード

evidence-upload スキルを使用して、スクリーンショットと GIF を pylot アセットバックエンドにアップロードします。$PYLOT_GATEWAY_URL と $PYLOT_DISPATCH_TOKEN は、すべてのオペレーター/ワーカー環境にすでに設定されているため、静的な AWS キーは必要ありません。

キャプチャした各ファイルに対して /evidence-upload を実行し、ステップ 4 で埋め込むために、返された public_url を取得します。

4. PR への埋め込み

修正前/修正後(バグ修正):

## Visual Evidence

<details>
<summary>Before / After</summary>

**Before (bug present on default branch):**
![before](<url>/before.png)

**After (fix applied):**
![after](<url>/after.png)

</details>

操作デモ(GIF):

## Visual Evidence

<details>
<summary>Interaction demo</summary>

![demo](<url>/demo.gif)
*Caption: what this demonstrates*

</details>

判断表:スクリーンショットと GIF

シナリオ形式
視覚的なレイアウト変更スクリーンショット(PNG)
新しいページ/機能スクリーンショット(PNG)
クリック時のボタン無効化GIF
読み込みスピナー/スケルトンGIF
ダブルクリック防止GIF
フォームの入力検証フィードバックGIF
トランジション/アニメーションGIF
エラー → 再試行のフローGIF

エラー処理

視覚的な証拠の取得はベストエフォートです。パイプラインを停止させてはいけません。

  • Playwright のインストールに失敗 → ログを記録し、証拠の取得を省略して続行
  • 開発サーバーが起動しない → ログを記録し、証拠の取得を省略して続行
  • スクリーンショットの取得に失敗(404、タイムアウト) → ログを記録し、証拠の取得を省略して続行
  • アップロードに失敗(認証情報なし、アクセス拒否) → ログを記録し、証拠の取得を省略して続行
  • ハードタイムアウト:証拠取得フェーズ全体で 120 秒。 超過した場合はプロセスを終了して続行します。

重要なルール

  • パイプラインを決して停止させない — 証拠の取得はベストエフォートです
  • ハードタイムアウトは 120 秒 — プロセスを終了して先に進みます
  • PR に URL を埋め込む前に、必ずアップロードを確認する
  • /tmp/ 内のスクリーンショットは一時的なものです — 環境の停止時に消去されます

コメント