From a4ec7e60a2e940564045c0cc0d3b81396d9d42f0 Mon Sep 17 00:00:00 2001 From: mizchi Date: Mon, 30 Mar 2026 11:09:27 +0900 Subject: [PATCH 01/17] wip --- src/adapters/mars_http/home_page.mbt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/adapters/mars_http/home_page.mbt b/src/adapters/mars_http/home_page.mbt index 7223580..e2261c5 100644 --- a/src/adapters/mars_http/home_page.mbt +++ b/src/adapters/mars_http/home_page.mbt @@ -277,11 +277,24 @@ fn render_workspace_spa(ws : String) -> String { #| const ta=document.getElementById('editor'); #| ta.addEventListener('keydown',e=>{if(e.key==='Tab'){e.preventDefault();const s=ta.selectionStart,end=ta.selectionEnd;ta.value=ta.value.substring(0,s)+' '+ta.value.substring(end);ta.selectionStart=ta.selectionEnd=s+2;}}); #|} + #|function simpleHash(s){let h=0;for(let i=0;i>>0).toString(16).padStart(8,'0');} #|async function saveEdit(){ #| const ta=document.getElementById('editor'); #| if(!ta)return; #| await idbPut(KEY_PFX+_editPath,ta.value); - #| navigate(subPath(),false); + #| await promptCommit(_editPath); + #|} + #|async function promptCommit(path){ + #| const msg=prompt('Commit message:','Edit '+path); + #| if(!msg){navigate(subPath(),false);return;} + #| const sha=simpleHash(msg+Date.now()+Math.random()); + #| const headSha=await idbGet('ws:'+WS_ID+':HEAD')||''; + #| const commit={sha,message:msg,author:'anonymous',date:new Date().toISOString(),parent:headSha,files:[{path,action:'modify'}]}; + #| await idbPut('ws:'+WS_ID+':commit:'+sha,JSON.stringify(commit)); + #| await idbPut('ws:'+WS_ID+':HEAD',sha); + #| const c=document.getElementById('spa-content'); + #| c.innerHTML='
Committed '+esc(sha.slice(0,8))+' — '+esc(msg)+'
'; + #| setTimeout(()=>navigate(subPath(),false),1500); #|} #| #|// --- Commits --- From 4b9822080873e15a383e32996547579dec68f0d8 Mon Sep 17 00:00:00 2001 From: mizchi Date: Mon, 30 Mar 2026 12:44:48 +0900 Subject: [PATCH 02/17] feat: add VRT + Semantic verification toolkit Independent vrt/ module with 116 tests for visual regression testing combined with accessibility semantic verification. Key components: - 3-track parallel pipeline (Diff Intent / Visual Semantic / A11y Semantic) - Cross-validation matrix (Visual x A11y x Intent) - 2-tier expectations (short-cycle per-commit + long-cycle spec invariants) - Introspect: auto-generate UI spec from a11y trees - Reasoning chains: expectation -> change -> realization verification - Dep graph integration for cost optimization (skip unaffected checks) - 10 fixture scenarios with harness quality tests (40 assertions) - Scoring system (usability / practicality / fixSteps / quality / tokens) - CLI workflow: init / capture / verify / approve / report Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 6 + TODO.md | 18 + docs/vrt-research.md | 128 ++++ justfile | 35 ++ vrt/SKILL.md | 195 +++++++ vrt/docs/architecture.md | 163 ++++++ vrt/docs/pipeline.md | 107 ++++ vrt/e2e/vrt-capture.spec.ts | 138 +++++ vrt/expectation.json | 21 + vrt/fixtures/react-sample/baseline.a11y.json | 46 ++ .../snapshot-a11y-fixed.a11y.json | 46 ++ .../snapshot-button-renamed.a11y.json | 46 ++ .../snapshot-form-removed.a11y.json | 37 ++ .../snapshot-heading-restructured.a11y.json | 48 ++ .../snapshot-label-broken.a11y.json | 46 ++ .../snapshot-nav-removed.a11y.json | 37 ++ .../snapshot-role-changed.a11y.json | 46 ++ .../snapshot-search-added.a11y.json | 54 ++ .../snapshot-section-added.a11y.json | 58 ++ .../snapshot-style-only.a11y.json | 46 ++ vrt/moon.mod.json | 11 + vrt/package.json | 18 + vrt/playwright.config.ts | 35 ++ vrt/pnpm-lock.yaml | 375 ++++++++++++ vrt/src/a11y-semantic.test.ts | 159 +++++ vrt/src/a11y-semantic.ts | 375 ++++++++++++ vrt/src/agent.test.ts | 99 ++++ vrt/src/agent.ts | 244 ++++++++ vrt/src/cli.ts | 337 +++++++++++ vrt/src/cross-validation.test.ts | 123 ++++ vrt/src/cross-validation.ts | 216 +++++++ vrt/src/dep-graph.test.ts | 129 +++++ vrt/src/dep-graph.ts | 315 ++++++++++ vrt/src/expectation.test.ts | 186 ++++++ vrt/src/expectation.ts | 400 +++++++++++++ vrt/src/harness.test.ts | 298 ++++++++++ vrt/src/heatmap.test.ts | 66 +++ vrt/src/heatmap.ts | 251 ++++++++ vrt/src/intent.test.ts | 113 ++++ vrt/src/intent.ts | 324 +++++++++++ vrt/src/introspect.test.ts | 159 +++++ vrt/src/introspect.ts | 322 ++++++++++ vrt/src/playwright-analyzer.ts | 180 ++++++ vrt/src/quality.ts | 210 +++++++ vrt/src/reasoning.test.ts | 235 ++++++++ vrt/src/reasoning.ts | 260 +++++++++ vrt/src/scenario.test.ts | 238 ++++++++ vrt/src/types.ts | 433 ++++++++++++++ vrt/src/visual-semantic.test.ts | 87 +++ vrt/src/visual-semantic.ts | 185 ++++++ vrt/src/vrt-cli.ts | 548 ++++++++++++++++++ vrt/tsconfig.json | 16 + 52 files changed, 8268 insertions(+) create mode 100644 docs/vrt-research.md create mode 100644 vrt/SKILL.md create mode 100644 vrt/docs/architecture.md create mode 100644 vrt/docs/pipeline.md create mode 100644 vrt/e2e/vrt-capture.spec.ts create mode 100644 vrt/expectation.json create mode 100644 vrt/fixtures/react-sample/baseline.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-a11y-fixed.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-button-renamed.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-form-removed.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-heading-restructured.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-label-broken.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-nav-removed.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-role-changed.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-search-added.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-section-added.a11y.json create mode 100644 vrt/fixtures/react-sample/snapshot-style-only.a11y.json create mode 100644 vrt/moon.mod.json create mode 100644 vrt/package.json create mode 100644 vrt/playwright.config.ts create mode 100644 vrt/pnpm-lock.yaml create mode 100644 vrt/src/a11y-semantic.test.ts create mode 100644 vrt/src/a11y-semantic.ts create mode 100644 vrt/src/agent.test.ts create mode 100644 vrt/src/agent.ts create mode 100644 vrt/src/cli.ts create mode 100644 vrt/src/cross-validation.test.ts create mode 100644 vrt/src/cross-validation.ts create mode 100644 vrt/src/dep-graph.test.ts create mode 100644 vrt/src/dep-graph.ts create mode 100644 vrt/src/expectation.test.ts create mode 100644 vrt/src/expectation.ts create mode 100644 vrt/src/harness.test.ts create mode 100644 vrt/src/heatmap.test.ts create mode 100644 vrt/src/heatmap.ts create mode 100644 vrt/src/intent.test.ts create mode 100644 vrt/src/intent.ts create mode 100644 vrt/src/introspect.test.ts create mode 100644 vrt/src/introspect.ts create mode 100644 vrt/src/playwright-analyzer.ts create mode 100644 vrt/src/quality.ts create mode 100644 vrt/src/reasoning.test.ts create mode 100644 vrt/src/reasoning.ts create mode 100644 vrt/src/scenario.test.ts create mode 100644 vrt/src/types.ts create mode 100644 vrt/src/visual-semantic.test.ts create mode 100644 vrt/src/visual-semantic.ts create mode 100644 vrt/src/vrt-cli.ts create mode 100644 vrt/tsconfig.json diff --git a/.gitignore b/.gitignore index 63e61a1..156f063 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,9 @@ playwright-report/ test-results/ .wrangler/ .bithub/ +vrt/test-results/ +vrt/baselines/ +vrt/snapshots/ +vrt/output/ +vrt/vrt-report.json +vrt/node_modules/ diff --git a/TODO.md b/TODO.md index 9f35a88..17f9a5f 100644 --- a/TODO.md +++ b/TODO.md @@ -61,6 +61,24 @@ Last updated: 2026-03-17 - [ ] Add `moon_ide` tool wrapping `moon ide peek-def` and `moon ide outline` - More accurate than grep for MoonBit symbol lookup +### VRT + Semantic Verification + +- [ ] Multi-step goal runner: 大きなゴールを分解して複数コミットで達成するシナリオテスト + - Goal → sub-tasks に分解 → 各 sub-task で expectation 生成 → 逐次実行 → ゴール達成を判定 + - 例: "ダークモード対応" → テーマ変数追加 → コンポーネント適用 → VRT 全ページ verify + - ランナー設計: `GoalRunner { goal, steps: Step[], currentStep, accumScore }` + - 各 step: expectation.json 自動生成 → subagent 実行 → verify → score → 次の step へ + - 失敗時: 修正ループ (max 3 retry) or ゴール縮小 + - 最終スコア: step 成功率 × 品質スコア × (1 / トークン消費) +- [ ] Playwright テストヘルパ: テスト失敗時のみ AI アサーション発火 + - `nlAssert(page, "ナビに5つ以上リンクがある", { onlyOnFailure: true, dependsOn: ["src/Header.tsx"] })` + - 失敗時: Vision LLM でスクリーンショット分析 → 修正ヒント → エージェントに投げる + - dep graph 連携で影響のないアサーションをスキップ +- [ ] introspect CLI コマンド: `vrt introspect` → spec.json 自動生成 +- [ ] spec verify CLI コマンド: `vrt spec-verify` → long-cycle 不変条件の検証 +- [ ] form/region ランドマークの spec invariant 自動生成 (現状 banner/main/nav のみ) +- [ ] role-changed の spec 検出 (現状は a11y diff でのみ検出) + ### Remaining features - [ ] Enable Container orchestration in wrangler.toml diff --git a/docs/vrt-research.md b/docs/vrt-research.md new file mode 100644 index 0000000..b2042da --- /dev/null +++ b/docs/vrt-research.md @@ -0,0 +1,128 @@ +# VRT (Visual Regression Testing) + AI 調査メモ + +## 動機 + +VRT で検出した差分を AI でリーズニングし、自己修復を行うアーキテクチャを検討する。 +ただしコストが高いため、以下で最適化したい: + +1. **依存ツリー解析** — 変更の影響範囲を絞り込み、スナップショット対象を最小化 +2. **意図とのすり合わせ** — commit/PR の変更意図と視覚差分を照合し、期待通りの変更を自動承認 + +--- + +## 1. 既存ツール・プラグインの状況 + +### AI-Powered VRT (商用) + +| ツール | アプローチ | 特徴 | +|--------|-----------|------| +| **Applitools Eyes** | 独自 Visual AI (CV ベース) | UI をセマンティックに理解。レンダリングノイズを無視。最も成熟 | +| **Percy Visual Review Agent** (BrowserStack) | スマートハイライト + 説明文生成 | 「ヘッダーが4px左にずれた」等の自然言語差分。偽陽性40%削減。人間承認は必要 | +| **Meticulous AI** | セッションリプレイ | ユーザ操作を記録→再生。静的スクリーンショットではなくフロー単位 | +| **TestMu SmartUI** | ピクセル + ヒューリスティクス | ルールベースの Smart Ignore (フォントレンダリング等) | + +### 依存ツリー対応 VRT + +| ツール | アプローチ | 特徴 | +|--------|-----------|------| +| **Chromatic TurboSnap** | Webpack/Vite Stats → モジュール依存グラフ | 変更ファイル → 影響 Story をマッピング。60-90% のスナップショット削減。**唯一の実装** | + +### OSS VRT ツール (ピクセルベース) + +- **Playwright** built-in VRT (pixelmatch) +- **Lost Pixel** — Storybook/Ladle 対応 +- **BackstopJS** — ビューポート/シナリオ設定 +- **reg-suit / reg-cli** — CI 統合・レポート生成 +- **Visual-Regression-Tracker** — セルフホスト。実験的 VLM サポートあり + +### ギャップ (未開拓領域) + +1. **LLM ベース VRT リーズニング**: スクリーンショット差分を Vision LLM に送り判定する OSS ツールは**存在しない** +2. **依存ツリー対応 VRT**: Chromatic TurboSnap の独占。Vite 単独や任意フレームワークで使える OSS 代替なし +3. **Intent-aware VRT**: コミットメッセージ/PR 記述から変更意図を抽出し、視覚差分と照合する仕組みは**完全に未開拓** + +--- + +## 2. 関連論文 + +### VRT 方法論・最適化 + +- Moradi et al. (2024) — **"AI for Context-Aware Visual Change Detection"** [arXiv:2405.00874](https://arxiv.org/abs/2405.00874) + - YOLOv5 で UI コントロールを検出 → グラフ構築 → 空間的文脈から意味のある変更を判定。ピクセル/リージョン比較より優秀 +- Web Application Testing Survey (2024) [arXiv:2412.10476](https://arxiv.org/abs/2412.10476) + - 2014-2023 の Web テスト研究サーベイ + +### 依存対応テスト選択 + +- **DIRTS** (ICST 2023) — DI フレームワーク対応の Regression Test Selection +- **Lam et al.** (ISSTA 2020) — 順序依存テストの優先付け・選択・並列化。依存対応アルゴリズムで失敗 80% 削減 +- **CORTS/C2RTS** (2025, J. Systems Architecture) — コンポーネントベース RTS。モジュールレベル依存グラフ + +### Self-Healing テスト自動化 + +- Chede & Tijare (2025, IJRASET) — **"AI-Driven Self-Healing UI Testing with Visual Proof"** + - DOM ベースのヒーリングとセマンティック視覚検証の統合 +- Self-Healing Test Automation with AI/ML (2024) — RL + 画像認識 + 動的ロケータ +- **SHML** (NeurIPS 2024) [arXiv:2411.00186](https://arxiv.org/abs/2411.00186) — 自律的診断・修復フレームワーク + +### LLM/Vision モデル × テスト + +- **Wang et al.** (TSE 2024) — LLM × ソフトウェアテスト 102 研究のサーベイ [arXiv:2307.07221](https://arxiv.org/abs/2307.07221) +- **VisionDroid** (2024) [arXiv:2407.03037](https://arxiv.org/abs/2407.03037) — マルチモーダル LLM で GUI 探索 + 非クラッシュバグ検出 +- **Make LLM a Testing Expert** (ICSE 2024) — 機能認識に基づくモバイル GUI テスト +- **RepairAgent** (ICSE 2025) — LLM ベースの自律プログラム修復エージェント +- Yu et al. (ACM Computing Surveys, 2025) — Vision-Based Mobile GUI Testing サーベイ [arXiv:2310.13518](https://arxiv.org/abs/2310.13518) + +### 参考リポジトリ + +- [LLM4SoftwareTesting](https://github.com/LLM-Testing/LLM4SoftwareTesting) — LLM × テスト論文キュレーション +- [GUI-Agents-Paper-List](https://github.com/OSU-NLP-Group/GUI-Agents-Paper-List) — GUI エージェント論文一覧 + +--- + +## 3. 考察: 本プロジェクトへの適用可能性 + +### 構想: 依存ツリー + Intent-aware + AI リーズニングの統合 + +``` +Code Change + │ + ├─ 1. 依存ツリー解析 (低コスト) + │ Vite モジュールグラフ / コンポーネント import 解析 + │ → 影響を受けるコンポーネント/ページを特定 + │ → VRT スナップショット対象を最小化 (TurboSnap 相当) + │ + ├─ 2. Intent 抽出 (低〜中コスト) + │ commit message / PR description を LLM でパース + │ → 「ボタンの色を青→緑に変更」等の期待変更を構造化 + │ + ├─ 3. VRT 実行 (中コスト) + │ 最小化されたスナップショット対象のみ撮影・比較 + │ + └─ 4. AI リーズニング (高コスト、条件付き) + Intent と一致する差分 → 自動承認 + Intent と不一致 or 予期しない差分 → Vision LLM で分析 + → 修復提案 or 人間レビューにエスカレート +``` + +### コスト最適化のポイント + +1. **段階的フィルタリング**: 依存ツリー → Intent 照合 → AI 判定 の順で、安価なフィルタから先に適用 +2. **AI 呼び出しの最小化**: 全差分を LLM に送るのではなく、自動承認できないものだけを AI に渡す +3. **キャッシュ**: 同一コンポーネントの類似差分パターンをキャッシュし、再判定を回避 + +### 技術的課題 + +- Vite モジュールグラフの取得方法 (TurboSnap は Webpack Stats API 依存) +- Intent 抽出の精度 (自然言語→構造化された期待変更の変換) +- Vision LLM のコスト vs 精度のトレードオフ +- self-healing の信頼性 (自動修復が意図しない変更を入れるリスク) + +--- + +## 4. 次のステップ + +- [ ] Vite プラグインとしての依存ツリー取得 PoC +- [ ] Playwright VRT + LLM リーズニングの最小プロトタイプ +- [ ] Intent 抽出プロンプトの設計 +- [ ] コスト試算 (スナップショット数 × LLM API コスト) diff --git a/justfile b/justfile index b827ac7..32b611c 100644 --- a/justfile +++ b/justfile @@ -70,6 +70,41 @@ agent-parallel: # ---- Workflow: Issue → Agent → Done ---- +# ---- VRT (Visual Regression + Semantic Testing) ---- + +# Initialize VRT baselines (requires running server) +vrt-init: + tsx vrt/src/vrt-cli.ts init + +# Capture current VRT snapshots +vrt-capture: + tsx vrt/src/vrt-cli.ts capture + +# Verify VRT snapshots against baselines +vrt-verify: + tsx vrt/src/vrt-cli.ts verify + +# Accept current snapshots as new baselines +vrt-approve: + tsx vrt/src/vrt-cli.ts approve + +# Show VRT report +vrt-report: + tsx vrt/src/vrt-cli.ts report + +# Show affected components +vrt-affected: + tsx vrt/src/vrt-cli.ts affected + +# Run VRT unit tests +vrt-test: + cd vrt && node --test --experimental-strip-types src/**/*.test.ts + +# Full VRT cycle: capture + verify +vrt: vrt-capture vrt-verify + +# ---- Workflow: Issue → Agent → Done ---- + # Full workflow: create issue, run daemon once auto title body="": #!/usr/bin/env bash diff --git a/vrt/SKILL.md b/vrt/SKILL.md new file mode 100644 index 0000000..6f44a67 --- /dev/null +++ b/vrt/SKILL.md @@ -0,0 +1,195 @@ +# VRT + Semantic Verification — Agent Skill Guide + +## 概要 + +Visual Regression Testing (VRT) とアクセシビリティセマンティクス検証を組み合わせた、 +コーディングエージェント向けの品質保証ツール。 + +変更が視覚的にもセマンティック(a11y)にも意図通りであることを自動検証し、 +リグレッションを検出・修復するループを回す。 + +## CLI コマンド + +すべて **プロジェクトルート** から実行する。サーバーが起動している前提。 + +```bash +# サーバー起動(別ターミナル) +just serve + +# 初回: ベースライン作成 +just vrt-init + +# 変更後: スナップショット取得 → 検証 +just vrt-capture +just vrt-verify +# または一括 +just vrt + +# 変更を承認: スナップショットを新ベースラインに昇格 +just vrt-approve + +# レポート確認 +just vrt-report + +# 影響範囲確認 +just vrt-affected +``` + +## エージェントのワークフロー + +### 基本ループ + +``` +┌─────────────────────────────────────────────┐ +│ 1. just vrt-init (初回のみ) │ +└─────────┬───────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────┐ +│ 2. コード変更を実施 │ +│ - commit message に意図を明記 │ +│ (feat: / fix: / style: / refactor: / │ +│ a11y: / deps:) │ +└─────────┬───────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────┐ +│ 3. just vrt │ +│ (= capture + verify) │ +└─────────┬───────────────────────────────────┘ + │ + ┌────┴────────────────┐ + │ │ + PASS FAIL/ESCALATE + │ │ + ▼ ▼ +┌──────────┐ ┌─────────────────────┐ +│ 4a. │ │ 4b. just vrt-report │ +│ approve │ │ → 問題を特定 │ +│ (任意) │ │ → コード修正 │ +└──────────┘ │ → 3 に戻る │ + └─────────────────────┘ +``` + +### 検証パイプライン(自動で実行される) + +``` +変更 ─→ 3 トラック並列実行: + +Track 1: Diff Intent — git diff + commit message → 変更意図を推測 +Track 2: Visual Diff — ピクセル比較 → ヒートマップ → 領域分類 +Track 3: A11y Diff — a11y ツリー差分 → セマンティック変化検出 + +→ Cross-Validation (3つの突き合わせ): + +| Visual | A11y | Intent | → 判定 | +|--------|-------|---------|----------------------| +| なし | なし | any | APPROVE (変化なし) | +| あり | あり | match | APPROVE (期待通り) | +| あり | あり | none | ESCALATE (意図不明) | +| あり | なし | style | APPROVE (見た目のみ) | +| あり | なし | refac | ESCALATE (意図しない) | +| なし | あり | a11y | APPROVE (a11y 改善) | +| なし | あり | other | REJECT (セマンティクス破壊) | +| any | regr | any | REJECT (a11y リグレ) | + +→ Quality Gate: + - 白飛び検出 (画面が真っ白) + - エラー状態検出 (赤い警告表示) + - 空コンテンツ検出 + - A11y リグレッション (ラベル消失、ランドマーク削除) + - VRT カバレッジ +``` + +## exit code + +| code | 意味 | +|------|------| +| 0 | PASS — 変化なし、または全て approved | +| 1 | FAIL — reject された変更あり、または品質エラー | + +escalate は exit 0 だが警告が出る。 + +## commit message の書き方 + +検証パイプラインは commit message から変更意図を推測する。 +意図が正しく推測されると、期待通りの視覚変化は自動承認される。 + +``` +feat: ダークモードトグル追加 → visual + a11y の追加が期待される +fix: モバイルでのレイアウト崩れ修正 → 修正対象のみの変化が期待される +refactor: ユーティリティ関数抽出 → visual/a11y ともに変化なしが期待される +style: ボタンの色を青→緑に変更 → visual 変化あり、a11y 変化なしが期待される +a11y: フォームにラベルを追加 → a11y 変化あり、visual 変化は最小限 +deps: React 19 にアップデート → visual/a11y ともに変化なしが期待される +``` + +## レポートの読み方 + +`just vrt-report` で出力される情報: + +``` +[APPROVE] home + Diff matches expected change: "color change" (confidence: 80%) + +[REJECT] settings + A11y regression detected: Removed button "Delete" + +[ESCALATE] profile + Visual change during refactor — appearance changed unexpectedly +``` + +- **APPROVE**: 期待通りの変更。問題なし。 +- **REJECT**: リグレッション検出。必ず修正が必要。 +- **ESCALATE**: 判断が分かれる変更。人間レビューまたは追加調査が必要。 + +## A11y チェックの活用 + +VRT verify は A11y ツリーも同時に検査する。以下が検出される: + +- ボタン/リンクにラベルがない (`label-missing`) +- 画像に alt テキストがない (`img-alt-missing`) +- ランドマーク要素の削除 (`landmark-changed`) +- インタラクティブ要素の削除 (`node-removed`) +- role の不適切な変更 (`role-changed`) + +リファクタリング中にこれらが検出された場合は、 +セマンティクスが壊れている可能性が高い。 + +## ファイル構成 + +``` +vrt/ +├── SKILL.md ← このファイル +├── package.json # 独立パッケージ +├── playwright.config.ts # VRT 用 Playwright 設定 +├── e2e/ +│ └── vrt-capture.spec.ts # スクリーンショット + a11y 収集 +├── src/ +│ ├── vrt-cli.ts # CLI エントリポイント +│ ├── types.ts # 全型定義 +│ ├── playwright-analyzer.ts # Playwright 出力解析 +│ ├── dep-graph.ts # 依存ツリー (TS/MoonBit/Rust) +│ ├── heatmap.ts # ピクセル比較 + ヒートマップ +│ ├── visual-semantic.ts # Visual Semantic Diff 分類 +│ ├── a11y-semantic.ts # A11y ツリー差分 + 品質チェック +│ ├── cross-validation.ts # Visual x A11y x Intent 突き合わせ +│ ├── intent.ts # Diff → 変更意図の推測 +│ ├── quality.ts # 品質ゲート +│ └── agent.ts # 5段階検証ループ +└── test-results/ # 実行結果 (gitignore 推奨) + ├── baselines/ # ベースライン PNG + a11y JSON + ├── snapshots/ # 最新スナップショット + ├── output/ # ヒートマップ等の出力 + └── vrt-report.json # 検証レポート +``` + +## トラブルシューティング + +| 問題 | 対処 | +|------|------| +| "Is the server running?" | `just serve` でサーバーを起動 | +| "No baselines found" | `just vrt-init` を実行 | +| フォントレンダリングの差分 | pixelmatch の threshold を調整 (heatmap.ts) | +| a11y ツリーが null | ページのレンダリング完了を待つ (waitFor 調整) | +| 全部 ESCALATE になる | commit message に prefix をつける (feat:/fix:/style: 等) | diff --git a/vrt/docs/architecture.md b/vrt/docs/architecture.md new file mode 100644 index 0000000..abfaa7a --- /dev/null +++ b/vrt/docs/architecture.md @@ -0,0 +1,163 @@ +# VRT + Semantic Verification — Architecture + +## 2層サイクル設計 + +```mermaid +graph TB + subgraph LongCycle["Long Cycle (spec.json)"] + direction TB + INTROSPECT["introspect
現在のUIからspec自動生成"] + SPEC["spec.json
不変条件 (invariants)"] + INTROSPECT --> SPEC + SPEC --> VERIFY_SPEC["verifySpec()
不変条件の検証"] + end + + subgraph ShortCycle["Short Cycle (expectation.json)"] + direction TB + EXP["expectation.json
このコミットで何が変わるか"] + EXP --> MATCH["matchA11yExpectation()
期待と実際の照合"] + MATCH --> CV["crossValidateWithExpectation()
Visual x A11y x Intent"] + end + + subgraph Pipeline["Verification Pipeline"] + direction TB + CAPTURE["capture
screenshots + a11y trees"] + DIFF["diff
baseline vs snapshot"] + CAPTURE --> DIFF + DIFF --> CV + DIFF --> VERIFY_SPEC + CV --> VERDICT["Unified Verdict"] + VERIFY_SPEC --> VERDICT + VERDICT --> SCORE["Score
usability / practicality / quality"] + end + + subgraph DepGraph["Dep Graph (コスト最適化)"] + CHANGED["Changed Files"] + GRAPH["Module Dependency Graph"] + CHANGED --> GRAPH + GRAPH --> SKIP{"Skip?"} + SKIP -->|unaffected| SKIP_CHECK["Skip invariant check"] + SKIP -->|affected| RUN_CHECK["Run check"] + SKIP -->|high-cost + unaffected| SKIP_NL["Skip NL assertion"] + end + + SKIP --> VERIFY_SPEC + + style LongCycle fill:#e8f5e9,stroke:#4CAF50 + style ShortCycle fill:#e8f4f8,stroke:#2196F3 + style Pipeline fill:#fff3e0,stroke:#FF9800 + style DepGraph fill:#f3e5f5,stroke:#9C27B0 +``` + +## 2層の役割分担 + +| | Short Cycle (expectation) | Long Cycle (spec) | +|---|---|---| +| **寿命** | 1コミット | 複数コミットにまたがる | +| **内容** | 「何が変わるか」 | 「何が常に成り立つべきか」 | +| **例** | "nav を消す" | "全ページに main ランドマークがある" | +| **regression** | 期待された regression を approve | 不変条件の violation を reject | +| **生成** | 人間 or エージェントが書く | introspect で自動生成 | +| **上書き** | spec の不変条件を一時的に上書き | expectation で上書き可能 | + +## Introspect フロー + +``` +現在の a11y ツリー + ↓ +introspect() + ↓ +PageIntrospection + - landmarks: banner, main, nav, ... + - interactive: 3 buttons, 5 links, 2 inputs + - unlabeled: 0 + - suggestedInvariants: [...] + ↓ +introspectToSpec() + ↓ +spec.json (UiSpec) + - pages: + - home: nav exists, all labeled, no whiteout + - about: main exists, heading h1 + - global: no whiteout, all labeled +``` + +## NL Assertion (将来) + +```typescript +// Playwright テスト内での使い方 (将来) +test("home page", async ({ page }) => { + await page.goto("/"); + + // 通常のアサーション (安価) + await expect(page.getByRole("heading")).toBeVisible(); + + // NL assertion (高価 — テスト失敗時のみ発火) + await nlAssert(page, "ナビゲーションバーに5つ以上のリンクがある", { + dependsOn: ["src/Header.tsx"], + onlyOnFailure: true, // 他のアサーションが落ちた時のみ実行 + }); +}); +``` + +### onlyOnFailure パターン + +```mermaid +graph LR + TEST["テスト実行"] --> PASS{"Pass?"} + PASS -->|Yes| DONE["完了"] + PASS -->|No| NL["NL Assertion 発火
(Vision LLM)"] + NL --> REPORT["修正ヒント付きレポート"] + REPORT --> AGENT["エージェントに投げる"] + AGENT --> FIX["修正 → 再テスト"] +``` + +### dep graph でのスキップ + +``` +変更ファイル: src/Footer.tsx + ↓ +dep graph 解析 + ↓ +src/Header.tsx は影響を受けない + ↓ +Header に依存する NL assertion はスキップ + ↓ +Footer に依存するアサーションのみ実行 +``` + +## コスト構造 + +| チェック種別 | コスト | 実行タイミング | +|---|---|---| +| ヒューリスティクス (whiteout, label) | 低 | 常に | +| ピクセル比較 (pixelmatch) | 中 | 常に | +| A11y ツリー diff | 低 | 常に | +| dep graph 解析 | 低 | 常に | +| Spec invariant 検証 | 低 | 影響ありの場合のみ | +| Visual Semantic 分類 | 低 | diff がある場合のみ | +| NL assertion (Vision LLM) | **高** | テスト失敗時 + 影響ありの場合のみ | +| LLM リーズニング | **高** | escalate された場合のみ | + +## ファイル構成 + +``` +vrt/ +├── expectation.json # Short cycle: このコミットの期待 +├── spec.json # Long cycle: 不変条件 (introspect で生成) +├── src/ +│ ├── types.ts # 全型定義 (UiSpec, NlAssertion 含む) +│ ├── introspect.ts # introspect + verifySpec +│ ├── expectation.ts # matchA11yExpectation + scoring +│ ├── cross-validation.ts # Visual x A11y x Intent +│ ├── dep-graph.ts # 依存ツリー (スキップ判定) +│ └── ... +├── fixtures/ +│ └── react-sample/ # 再現可能テスト fixtures +│ ├── baseline.a11y.json +│ ├── snapshot-nav-removed.a11y.json +│ └── snapshot-label-broken.a11y.json +└── docs/ + ├── pipeline.md # パイプライン図 + └── architecture.md # この文書 +``` diff --git a/vrt/docs/pipeline.md b/vrt/docs/pipeline.md new file mode 100644 index 0000000..530bed3 --- /dev/null +++ b/vrt/docs/pipeline.md @@ -0,0 +1,107 @@ +# VRT + Semantic Verification Pipeline + +## 全体設計 + +3つの独立した Diff ソースを並列に生成し、統合検証で突き合わせる。 + +```mermaid +graph TB + subgraph Input["入力ソース"] + GIT["Git Diff
(code change)"] + PW["Playwright Execution"] + end + + subgraph Parallel["並列パイプライン"] + direction TB + + subgraph Track_Intent["Track 1: Diff Intent"] + GIT --> PARSE_DIFF["Parse Unified Diff"] + PARSE_DIFF --> DEP_GRAPH["Dependency Graph
(TS / MoonBit / Rust)"] + DEP_GRAPH --> AFFECTED["Affected Components"] + PARSE_DIFF --> INTENT["Change Intent
ヒューリスティック or LLM"] + end + + subgraph Track_Visual["Track 2: Visual Semantic Diff"] + PW --> SCREENSHOT["Screenshots
(current + baseline)"] + SCREENSHOT --> PIXEL_DIFF["Pixel Diff
(pixelmatch)"] + PIXEL_DIFF --> HEATMAP["Heatmap +
Region Detection"] + HEATMAP --> VIS_SEM["Visual Semantic Diff
- 領域分類 (text/icon/layout/color)
- 変化の性質 (added/removed/moved/restyled)"] + end + + subgraph Track_A11y["Track 3: Accessibility Semantic Diff"] + PW --> A11Y_TREE["A11y Tree Snapshot
(current + baseline)"] + A11Y_TREE --> A11Y_DIFF["A11y Tree Diff
- ノード追加/削除/変更
- role, name, state 変化"] + A11Y_DIFF --> A11Y_SEM["A11y Semantic Diff
- ARIA 契約の検証
- ナビゲーション構造変化
- ラベル/ランドマーク整合性"] + end + end + + subgraph Merge["統合検証"] + VIS_SEM --> CROSS["Cross-Validation
Visual ↔ A11y 整合性チェック"] + A11Y_SEM --> CROSS + INTENT --> JUDGE["Unified Verdict Engine"] + AFFECTED --> JUDGE + CROSS --> JUDGE + JUDGE --> VERDICTS["Verdicts
(approve / reject / escalate)"] + end + + subgraph Quality["品質ゲート"] + VERDICTS --> QC["Quality Checks"] + QC --> WH["白飛び検出"] + QC --> ERR["エラー状態検出"] + QC --> COV["VRT + A11y カバレッジ"] + QC --> A11Y_REG["A11y リグレッション
(role欠損, label消失)"] + QC --> REPORT["Verification Report"] + end + + style Track_Intent fill:#e8f4f8,stroke:#2196F3 + style Track_Visual fill:#fff3e0,stroke:#FF9800 + style Track_A11y fill:#e8f5e9,stroke:#4CAF50 + style Merge fill:#f3e5f5,stroke:#9C27B0 + style Quality fill:#fce4ec,stroke:#E91E63 +``` + +## Cross-Validation マトリクス + +Visual Diff と A11y Diff の突き合わせで、変更の妥当性を判定する。 + +| Visual Diff | A11y Diff | Intent Match | 判定 | +|-------------|-----------|-------------|------| +| なし | なし | any | **Auto-approve** (変化なし) | +| あり | あり | あり | **Auto-approve** (期待通り) | +| あり | あり | なし | **Escalate** (意図しない変更) | +| あり | なし | style | **Approve** (見た目のみの変更、セマンティクス維持) | +| あり | なし | refactor | **Warning** (リファクタなのに見た目が変化) | +| なし | あり | any | **Reject** (見た目は同じだがセマンティクス破壊) | +| any | regression | any | **Reject** (A11y リグレッション) | + +## データフロー詳細 + +### Visual Semantic Diff + +ピクセル差分の「意味」を分類: +- **text-change**: テキスト領域の変化 (OCR ベースの検出) +- **color-change**: 色のみの変化 (形状は同一) +- **layout-shift**: 要素の位置移動 +- **element-added**: 新しい要素の出現 +- **element-removed**: 要素の消失 +- **icon-change**: アイコン/画像の変化 + +### Accessibility Semantic Diff + +A11y ツリーの構造差分: +- **node-added**: 新しい a11y ノード +- **node-removed**: ノードの消失 (リグレッション候補) +- **role-changed**: role 属性の変化 +- **name-changed**: accessible name の変化 +- **state-changed**: aria-* 状態の変化 +- **structure-changed**: ツリー構造の変化 (親子関係) +- **landmark-changed**: ランドマーク (