Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions .claude/commands/build-from-figma.md
Original file line number Diff line number Diff line change
Expand Up @@ -278,21 +278,20 @@ Generate and run end-to-end tests appropriate to the app type.

## Phase 7: Cross-Browser Verification (Non-Blocking)

Capture screenshots in Firefox and WebKit, compare against Chromium baseline.
Diff Firefox and WebKit screenshots against their committed baselines with
provenance verification (RFC 0002).

```bash
# Only for web apps and PWAs (Chrome extensions are Chromium-only)
./scripts/cross-browser-test.sh firefox http://localhost:3000
./scripts/cross-browser-test.sh webkit http://localhost:3000

node scripts/visual-diff.js --batch \
.claude/visual-qa/screenshots/firefox \
.claude/visual-qa/screenshots/chromium \
--output-dir .claude/visual-qa/diffs/firefox-vs-chromium \
--threshold 0.03
./scripts/cross-browser-baseline.sh compare http://localhost:3000 --json
```

Cross-browser differences are logged in the build report but do NOT block the pipeline.
If no cross-browser baselines are committed yet the compare skips with a
capture hint (`./scripts/cross-browser-baseline.sh capture <url>` in the
pinned Playwright container, then commit `.claude/visual-qa/baselines/`).

Cross-browser differences are logged in the build report but do NOT block the
pipeline (`visualBaselines.blocking: false`).

## Phase 8: Quality Gate

Expand Down
39 changes: 37 additions & 2 deletions .claude/pipeline.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,41 @@
"browsers": ["chromium"],
"reportFile": "regression-report.md"
},
"visualBaselines": {
"enabled": true,
"backend": "commit",
"storage": "git",
"baselineDir": ".claude/visual-qa/baselines",
"screenshotDir": ".claude/visual-qa/screenshots/cross-browser",
"diffDir": ".claude/visual-qa/diffs/cross-browser",
"browsers": ["chromium", "firefox", "webkit"],
"routes": ["/"],
"breakpoints": {
"mobile": 375,
"desktop": 1440
},
"threshold": 0.03,
"blocking": false,
"reportFile": "cross-browser-report.md",
"capture": {
"mode": "container",
"image": "mcr.microsoft.com/playwright:v1.61.1-noble",
"waitAfterLoadMs": 1500,
"fullPage": true
},
"provenance": {
"manifest": ".claude/visual-qa/baselines/manifest.json",
"policy": "warn"
},
"ciArtifact": {
"compareAgainst": "last-green-main",
"retentionDays": 30
},
"service": {
"provider": "chromatic",
"projectTokenEnv": "CHROMATIC_PROJECT_TOKEN"
}
},
"deadCode": {
"enabled": true,
"tool": "knip",
Expand Down Expand Up @@ -472,9 +507,9 @@
},
"cross-browser": {
"depends": ["component-build"],
"resources": [],
"resources": [{ "name": "port:dev-server", "mode": "shared" }],
"blocking": false,
"description": "Firefox and WebKit screenshot comparison"
"description": "Firefox and WebKit baseline comparison (cross-browser-baseline.sh compare)"
},
"quality-gate": {
"depends": ["component-build"],
Expand Down
72 changes: 72 additions & 0 deletions .claude/pipeline.config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,78 @@
}
},

"visualBaselines": {
"type": "object",
"additionalProperties": false,
"description": "Cross-browser (firefox/webkit) screenshot baseline storage per RFC 0002: pluggable backend, deterministic containerized capture, and per-baseline provenance. Same-browser chromium regression stays in regressionTesting.",
"properties": {
"enabled": { "type": "boolean" },
"backend": {
"type": "string",
"enum": ["commit", "ci-artifact", "service"],
"description": "Where baselines live: committed to the repo (default), fetched from the last-green-main CI artifact, or delegated to a SaaS provider."
},
"storage": {
"type": "string",
"enum": ["git", "lfs"],
"description": "commit backend only: plain git-tracked PNGs, or Git LFS for large baseline sets (see scripts/setup-baseline-lfs.sh)."
},
"baselineDir": { "type": "string", "description": "Baseline root, shared with regressionTesting; per-engine subdirectories." },
"screenshotDir": { "type": "string", "description": "Transient current-capture directory (gitignored)." },
"diffDir": { "type": "string", "description": "Transient diff-image directory (gitignored)." },
"browsers": { "$ref": "#/$defs/browserList" },
"routes": { "type": "array", "items": { "type": "string" } },
"breakpoints": { "$ref": "#/$defs/breakpointMap" },
"threshold": { "$ref": "#/$defs/percentageRatio", "description": "Cross-engine mismatch tolerance. Must equal e2e.crossBrowserDiffThreshold (structural check)." },
"blocking": { "type": "boolean", "description": "When true, compare failures exit non-zero (Phase B teeth). Keep false until determinism is proven." },
"reportFile": { "type": "string" },
"capture": {
"type": "object",
"additionalProperties": false,
"properties": {
"mode": {
"type": "string",
"enum": ["container", "local"],
"description": "container (required for trustworthy firefox/webkit baselines) runs capture in the pinned Playwright image; local captures are recorded with host=local and flagged by provenance."
},
"image": { "type": "string", "description": "Pinned Playwright container image (authoritative; scripts warn on drift vs the resolved @playwright/test version)." },
"waitAfterLoadMs": { "type": "integer", "minimum": 0 },
"fullPage": { "type": "boolean" }
}
},
"provenance": {
"type": "object",
"additionalProperties": false,
"properties": {
"manifest": { "type": "string", "description": "Committed provenance manifest path (engine, Playwright version, image, sha256, gitSha, host per baseline)." },
"policy": {
"type": "string",
"enum": ["warn", "enforce"],
"description": "warn: provenance mismatches are reported but baselines are still diffed. enforce: flagged baselines are excluded from diffing and counted as failures."
}
}
},
"ciArtifact": {
"type": "object",
"additionalProperties": false,
"description": "backend=ci-artifact only.",
"properties": {
"compareAgainst": { "type": "string", "enum": ["last-green-main"] },
"retentionDays": { "type": "integer", "minimum": 1 }
}
},
"service": {
"type": "object",
"additionalProperties": false,
"description": "backend=service only. The provider owns capture, diffing, and review; opt-in, requires a token in CI secrets.",
"properties": {
"provider": { "type": "string", "enum": ["chromatic", "percy"] },
"projectTokenEnv": { "type": "string", "description": "Name of the environment variable holding the provider project token." }
}
}
}
},

"deadCode": {
"type": "object",
"additionalProperties": false,
Expand Down
33 changes: 20 additions & 13 deletions .claude/skills/figma-to-react-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,22 +480,28 @@ For each page in build-spec:

### Step 3.2.5: Cross-Browser Verification

After Chromium passes the visual diff loop, verify in other browsers:
After Chromium passes the visual diff loop, diff Firefox and WebKit against
their committed baselines (RFC 0002):

```bash
# Capture screenshots in Firefox and WebKit
./scripts/cross-browser-test.sh firefox http://localhost:3000
./scripts/cross-browser-test.sh webkit http://localhost:3000

# Compare against Chromium baseline (higher threshold for browser differences)
node scripts/visual-diff.js --batch \
.claude/visual-qa/screenshots/firefox \
.claude/visual-qa/screenshots/chromium \
--output-dir .claude/visual-qa/diffs/firefox-vs-chromium \
--threshold 0.03
# Compare current firefox/webkit screenshots against committed baselines
# (threshold from visualBaselines.threshold, 0.03 for cross-engine tolerance)
./scripts/cross-browser-baseline.sh compare http://localhost:3000 --json
```

Cross-browser results are logged in the build report but are **not blocking** by default.
If no cross-browser baselines exist yet, the compare skips with a hint —
capture them deterministically first and commit the result:

```bash
./scripts/cross-browser-baseline.sh capture http://localhost:3000 # pinned Playwright container
git add .claude/visual-qa/baselines && git commit -m "test: add cross-browser baselines"
```

Each baseline's provenance (engine, Playwright version, container image,
sha256, host) is verified from `baselines/manifest.json` before diffing, so
stale or locally-captured baselines are flagged instead of producing false
pixel diffs. Results are logged in the build report but are **not blocking**
by default (`visualBaselines.blocking`).

### Step 3.3: E2E Tests

Expand Down Expand Up @@ -686,7 +692,8 @@ This skill works with:
- **scripts/visual-diff.js** — Pixel-level screenshot comparison with region analysis
- **scripts/verify-tokens.sh** — Token integrity enforcement in quality gate
- **scripts/verify-test-coverage.sh** — Test existence verification (TDD gate)
- **scripts/cross-browser-test.sh** — Multi-browser screenshot capture
- **scripts/cross-browser-baseline.sh** — Cross-browser baseline capture + provenance-verified comparison (RFC 0002)
- **scripts/cross-browser-test.sh** — Ad-hoc multi-browser screenshot capture (no comparison)
- **scripts/generate-stories.sh** — AST-based Storybook story + MDX generation (Phase 4.5)
- **.claude/pipeline.config.json** — Thresholds, iteration limits, app type config

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/parallel-orchestration/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ function hasResourceConflict(candidatePhase, runningPhases):
| `visual-diff` | **Agent:** invoke visual-qa-verification skill with pixel-diff loop (max iterations from `iterationLoop.maxVisualIterations`) |
| `dark-mode` | **Bash:** `./scripts/check-dark-mode.sh http://localhost:3000` |
| `e2e-tests` | **Agent:** invoke e2e-test-generator skill with flows from build-spec.json |
| `cross-browser` | **Bash:** `./scripts/cross-browser-test.sh firefox http://localhost:3000 && ./scripts/cross-browser-test.sh webkit http://localhost:3000` |
| `cross-browser` | **Bash:** `./scripts/cross-browser-baseline.sh compare http://localhost:3000 --json` (diffs firefox/webkit against committed baselines with provenance checks — RFC 0002; skips with a capture hint when no baselines exist) |
| `quality-gate` | **Parallel sub-dispatch:** run all `qualityGateSubtasks` concurrently (see below) |
| `responsive` | **Bash:** `./scripts/check-responsive.sh http://localhost:3000` |
| `report` | **Agent:** generate `build-report.md` in `.claude/visual-qa/` from all collected phase results, timings, and diff images |
Expand Down
44 changes: 19 additions & 25 deletions .claude/skills/visual-qa-verification/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,35 +143,28 @@ When the diff reports failing regions, use this mapping to prioritize fixes:

### Step 2.5: Cross-Browser Verification

After Chromium passes the visual diff loop, verify in Firefox and WebKit.

**Using Playwright MCP or cross-browser-test.sh:**
After Chromium passes the visual diff loop, diff Firefox and WebKit against
their committed same-engine baselines (RFC 0002):

```bash
# Capture Firefox screenshots
./scripts/cross-browser-test.sh firefox http://localhost:3000

# Capture WebKit screenshots
./scripts/cross-browser-test.sh webkit http://localhost:3000

# Compare Firefox against Chromium baseline (not Figma)
node scripts/visual-diff.js --batch \
.claude/visual-qa/screenshots/firefox \
.claude/visual-qa/screenshots/chromium \
--output-dir .claude/visual-qa/diffs/firefox-vs-chromium \
--threshold 0.03

# Compare WebKit against Chromium baseline
node scripts/visual-diff.js --batch \
.claude/visual-qa/screenshots/webkit \
.claude/visual-qa/screenshots/chromium \
--output-dir .claude/visual-qa/diffs/webkit-vs-chromium \
--threshold 0.03
# Compare current firefox/webkit screenshots against committed baselines,
# with per-baseline provenance verification from baselines/manifest.json
./scripts/cross-browser-baseline.sh compare http://localhost:3000 --json

# First time (no baselines committed yet): capture deterministically in the
# pinned Playwright container, then commit
./scripts/cross-browser-baseline.sh capture http://localhost:3000
git add .claude/visual-qa/baselines

# Provenance-only check (no dev server needed)
./scripts/cross-browser-baseline.sh verify
```

**Cross-browser threshold:** Use a slightly higher threshold (3% vs 2%) since browser rendering engines have legitimate differences in anti-aliasing, font rendering, and sub-pixel positioning.
For ad-hoc engine captures without comparison, `./scripts/cross-browser-test.sh <browser> <url>` still works.

**Cross-browser threshold:** `visualBaselines.threshold` (3% vs the 2% same-browser threshold) since browser rendering engines have legitimate differences in anti-aliasing, font rendering, and sub-pixel positioning.

**Cross-browser failures are NOT blocking** by default (configurable in `pipeline.config.json` → `qualityGate.crossBrowserScreenshotsRequired`). They are reported in the build report for manual review.
**Cross-browser failures are NOT blocking** by default (`visualBaselines.blocking`, and `qualityGate.crossBrowserScreenshotsRequired` for the gate). They are reported in the build report for manual review. Compares run outside the pinned container are marked *advisory* by the provenance check.

**Common cross-browser differences to ignore:**
- Font rendering (especially on macOS WebKit vs Windows Chromium)
Expand Down Expand Up @@ -349,7 +342,8 @@ This skill works with:
- **Playwright MCP** -- Cross-browser screenshots (Firefox, WebKit) and automated interaction testing
- **Figma MCP** -- Source design screenshots for comparison (`get_screenshot`, `get_design_context`)
- **scripts/visual-diff.js** -- Pixel-level screenshot comparison with region analysis
- **scripts/cross-browser-test.sh** -- Multi-browser screenshot capture
- **scripts/cross-browser-baseline.sh** -- Cross-browser baseline capture + provenance-verified comparison (RFC 0002)
- **scripts/cross-browser-test.sh** -- Ad-hoc multi-browser screenshot capture (no comparison)
- **.claude/pipeline.config.json** -- Thresholds, iteration limits, breakpoint configuration

## Verification Report Template
Expand Down
Loading
Loading