Iterative migration workflow with parity checks, tests, and code review
Compares legacy and new Vue apps for visual and functional parity using automated testing.
/plugin marketplace add josephanson/claude-plugin/plugin install ja@josephanson<legacy-url> <new-url> [--pages page1,page2] [--video] [--max-iterations N]Combines Ralph Wiggum loops with Claude's Chrome browser automation for iterative legacy→Vue migration validation.
/ja:migrate-parity <legacy-url> <new-url> [options]
Note: Pass full URLs including paths - legacy and new paths can differ.
| Argument | Required | Default | Description |
|---|---|---|---|
legacy-url | Yes | - | Full legacy URL (source of truth) |
new-url | Yes | - | Full new Vue URL |
--pages | No | - | Comma-separated additional pages/paths to test |
--video | No | false | Enable GIF recording for temporal bugs (flickering, animations) |
--max-iterations | No | 20 | Ralph loop safety limit |
brew install imagemagick or apt-get install imagemagick)# Single page comparison (different paths allowed)
/ja:migrate-parity https://legacy.provetcloud.com/4/organization/administration/catalog/item_lists/ http://localhost:5171/4/catalog/management/item-lists
# Multiple pages with video recording
/ja:migrate-parity https://legacy.example.com/ http://localhost:3000/ --pages /about,/products,/contact --video
# Full site comparison with increased iterations
/ja:migrate-parity https://legacy.example.com/ http://localhost:3000/ --pages /dashboard,/settings,/profile --max-iterations 30
Extract from $ARGUMENTS:
legacy-url (required) - full URL including pathnew-url (required) - full URL including path--pages (optional) - comma-separated paths (e.g., "/about,/products,/contact")--video (optional) - enable GIF recording for each page comparison--max-iterations (default: 20)Build page list:
[{legacy: legacy-url, new: new-url}]--pages provided, add each: [{legacy: legacy-base + page, new: new-base + page}]Before proceeding, verify required tools are installed:
# Check ImageMagick
which compare || echo "ERROR: ImageMagick not installed. Run: brew install imagemagick"
# Check Chrome extension (will be verified when first tool is called)
Scan working directory for project directories containing:
package.json (JS/TS project)pyproject.toml or requirements.txt (Python project)For each detected project, auto-detect test command:
pnpm-lock.yaml exists → pnpm testpackage-lock.json exists → npm testpyproject.toml exists → pytestFor each detected project, auto-detect format command:
.pre-commit-config.yaml exists → pre-commit run --all-filespnpm-lock.yaml exists → pnpm lint:fixpyproject.toml exists → ruff formatnpm run lint:fixFor each detected project, auto-detect typecheck command:
tsconfig.json exists → pnpm typecheck or npm run typecheckpyproject.toml with mypy config → mypy or just typecheckpackage.json scripts for typecheck or type-check scriptImportant: Frontend projects often generate types from backend APIs. When backend APIs change, frontend types must be regenerated. Look for:
pnpm generate:types or similar scripts in package.jsonCreate .claude/ralph-loop.local.md with this content:
---
active: true
iteration: 1
max_iterations: {max-iterations}
completion_promise: "MIGRATION_PARITY_ACHIEVED"
started_at: "{ISO timestamp}"
---
Migration parity check for:
- Legacy: {legacy-url}
- New: {new-url}
- Projects: {projects}
- Test cmds: {test-cmds}
- Format cmds: {format-cmds}
- Typecheck cmds: {typecheck-cmds}
Check parity, fix issues, run typecheck, run tests for all projects, format code.
Output <promise>MIGRATION_PARITY_ACHIEVED</promise> when complete.
Create .claude/.migrate-parity-state.md:
# Migration Parity State
## Configuration
- Legacy Base URL: {legacy-url}
- New Base URL: {new-url}
- Video Recording: {enabled/disabled}
- Pages to Test: {count}
## Projects
| Project | Test Command | Format Command | Typecheck Command |
|---------|--------------|----------------|-------------------|
{for each project: | {project} | {test-cmd} | {format-cmd} | {typecheck-cmd} |}
## Parity Status by Page
| Page | Status | Similarity | Critical | Major | Minor | Diff Image | Last Check |
|------|--------|------------|----------|-------|-------|------------|------------|
| {page-1} | PENDING | - | 0 | 0 | 0 | - | - |
| {page-2} | PENDING | - | 0 | 0 | 0 | - | - |
## Overall Parity Health
- Status: PENDING
- Total Pages: {count}
- Pages with CRITICAL issues: 0
- Pages with MAJOR issues: 0
- Pages with MINOR issues only: 0
- Pages with no issues: 0
## Typecheck
| Project | Status | Last Run |
|---------|--------|----------|
{for each project: | {project} | PENDING | - |}
## Tests
| Project | Status | Last Run |
|---------|--------|----------|
{for each project: | {project} | PENDING | - |}
## Iteration: 1 of {max-iterations}
Use Claude's Chrome browser automation to compare legacy and new URLs.
Get browser tab context:
mcp__claude-in-chrome__tabs_context_mcp with createIfEmpty: true
Create a new tab for this comparison:
mcp__claude-in-chrome__tabs_create_mcp
If --video flag enabled, start GIF recording:
mcp__claude-in-chrome__gif_creator with action: "start_recording"
For each page in the page list (main URL + any --pages):
Navigate to legacy URL and capture state:
mcp__claude-in-chrome__navigate to {legacy-url}
mcp__claude-in-chrome__wait_for with time: 2 (let page fully load)
mcp__claude-in-chrome__snapshot (save to .claude/parity-{page-slug}-legacy-snapshot.md)
mcp__claude-in-chrome__take_screenshot (save as .claude/parity-{page-slug}-legacy.png)
Scroll and capture additional content (if page has scroll):
mcp__claude-in-chrome__computer with action: "scroll", scroll_direction: "down"
mcp__claude-in-chrome__take_screenshot (save as .claude/parity-{page-slug}-legacy-scrolled.png)
Navigate to new URL and capture state:
mcp__claude-in-chrome__navigate to {new-url}
mcp__claude-in-chrome__wait_for with time: 2 (let page fully load)
mcp__claude-in-chrome__snapshot (save to .claude/parity-{page-slug}-new-snapshot.md)
mcp__claude-in-chrome__take_screenshot (save as .claude/parity-{page-slug}-new.png)
Scroll and capture additional content (if page has scroll):
mcp__claude-in-chrome__computer with action: "scroll", scroll_direction: "down"
mcp__claude-in-chrome__take_screenshot (save as .claude/parity-{page-slug}-new-scrolled.png)
If --video enabled, stop and export recording:
mcp__claude-in-chrome__gif_creator with action: "stop_recording"
mcp__claude-in-chrome__gif_creator with action: "export", download: true, filename: "parity-{page-slug}.gif"
For each page comparison:
Automated Screenshot Comparison (initial viewport):
compare -metric RMSE -fuzz 5% \
.claude/parity-{page-slug}-legacy.png \
.claude/parity-{page-slug}-new.png \
.claude/parity-{page-slug}-diff.png 2>&1 | tee .claude/parity-{page-slug}-score.txt
-fuzz 5% tolerates minor anti-aliasing/font rendering differencesAutomated Screenshot Comparison (scrolled state):
compare -metric RMSE -fuzz 5% \
.claude/parity-{page-slug}-legacy-scrolled.png \
.claude/parity-{page-slug}-new-scrolled.png \
.claude/parity-{page-slug}-diff-scrolled.png 2>&1
Parse Similarity Score:
100 - (RMSE / max_possible_error * 100)Manual Review (if needed):
Document Results in .claude/.migrate-parity-state.md:
Combine automated similarity scores with manual review:
Automated Classification (from similarity score):
Manual Classification Criteria (review diff image + accessibility tree):
Final Decision:
After all pages tested, calculate migration health:
Migration health: PASS if no CRITICAL/MAJOR issues, otherwise FAIL
For each FAIL result:
Focus on:
Critical for API changes: When backend API changes, frontend types must be regenerated first.
For frontend projects with API type generation:
# First regenerate types from backend API (if applicable)
cd {frontend-project-dir} && {type-generation-cmd} # e.g., pnpm generate:types
# Then run typecheck
cd {frontend-project-dir} && {typecheck-cmd} # e.g., pnpm typecheck
For backend projects:
cd {backend-project-dir} && {typecheck-cmd} # e.g., mypy or just typecheck
If typecheck fails:
Update state file with typecheck results for each project.
For each project, execute its test command:
# For each project directory
cd {project-dir} && {test-cmd}
If tests fail in any project:
Update state file with test results for each project.
For each project, execute its format command:
# For each project directory
cd {project-dir} && {format-cmd}
Ensure no lint errors remain in any project.
Before declaring complete, run /ja:pre-commit --skip-mr to ensure all quality gates pass.
Read .claude/.migrate-parity-state.md. Check if:
If NOT complete:
If COMPLETE:
pr-review-toolkit:code-reviewer agent for final reviewWhen all conditions met:
## Migration Parity Complete
### Overall Health Assessment
- Status: PASS
- Total Pages Tested: {count}
- Pages with CRITICAL issues: 0
- Pages with MAJOR issues: 0
- Pages with MINOR issues only: {count}
- Pages with no issues: {count}
### Parity Results by Page
| Page | Status | Similarity | Critical | Major | Minor | Diff Image |
|------|--------|------------|----------|-------|-------|------------|
| {page-1} | PASS | 98.7% | 0 | 0 | 2 | .claude/parity-{page-1}-diff.png |
| {page-2} | PASS | 99.2% | 0 | 0 | 0 | .claude/parity-{page-2}-diff.png |
### Typecheck Results
| Project | Status | Command |
|---------|--------|---------|
{for each project: | {project} | PASS | {typecheck-cmd} |}
### Test Results
| Project | Status | Command |
|---------|--------|---------|
{for each project: | {project} | PASS | {test-cmd} |}
### Code Review: APPROVED
### Artifacts Generated
- Accessibility snapshots: `.claude/parity-*-snapshot.md`
- Screenshots: `.claude/parity-*.png`
- Diff images: `.claude/parity-*-diff.png` (visual difference highlights)
- Similarity scores: `.claude/parity-*-score.txt`
{if --video: - GIF recordings: `parity-*.gif` (in downloads)}
**Review Tip**: Open diff images to see exact visual differences highlighted in red.
<promise>MIGRATION_PARITY_ACHIEVED</promise>
| Scenario | Action |
|---|---|
| Server not running | Prompt user to start servers |
| URL 404 | Mark as SKIP, warn user |
| Flaky tests | Retry up to 3 times |
| Max iterations reached | Report progress, suggest manual completion |
| Parity check timeout | Retry with increased timeout |
| Project not found | Warn user, skip that project |
| Mixed test results | Continue until all projects pass |
| Typecheck fails | Fix type errors before running tests |
| Frontend types out of sync | Regenerate types from backend API first |
| API type generation fails | Check backend server is running, schema is valid |
| Chrome extension not available | Prompt user to install Claude in Chrome extension |
| Chrome tab context lost | Re-establish tab context with tabs_context_mcp |
| Screenshot/snapshot capture fails | Retry up to 3 times, report error if persistent |
| Browser navigation timeout | Increase wait time, check if page is loading |
| ImageMagick not installed | Prompt user to install: brew install imagemagick or apt-get install imagemagick |
| ImageMagick compare fails | Check screenshot files exist, retry with different fuzz tolerance |
| Screenshots different dimensions | ImageMagick handles this, but warn about viewport size differences |
.claude/ralph-loop.local.md - Ralph loop state (iteration, promise).claude/.migrate-parity-state.md - Migration progress trackingFor each page tested, the following files are generated:
Accessibility Trees:
.claude/parity-{page-slug}-legacy-snapshot.md - Legacy page accessibility tree.claude/parity-{page-slug}-new-snapshot.md - New page accessibility treeScreenshots:
.claude/parity-{page-slug}-legacy.png - Legacy page screenshot (initial viewport).claude/parity-{page-slug}-new.png - New page screenshot (initial viewport).claude/parity-{page-slug}-legacy-scrolled.png - Legacy page screenshot (scrolled).claude/parity-{page-slug}-new-scrolled.png - New page screenshot (scrolled)Comparison Results:
.claude/parity-{page-slug}-diff.png - Visual diff image (initial viewport, differences highlighted in red).claude/parity-{page-slug}-diff-scrolled.png - Visual diff image (scrolled state).claude/parity-{page-slug}-score.txt - RMSE similarity scoreVideo (if --video enabled):
parity-{page-slug}.gif - GIF recording of comparison (saved to downloads)