code-migration
Skill2 starsChanged 7 months agoApache-2.0View on GitHub
Migrate applications between technologies using kantra static analysis and automated fixes. Use when migrating Java, Node.js, Python, Go, or .NET applications. Keywords: kantra, migration, upgrade, modernize.
- Reads credentials
5 other projects use a version of this.
Adopted by
Most recently taken up by
- ishandutta2007/Awesome-Claude-Skillstheir own version, first seen Nov 8, 2026
- tejastn10/workbenchtheir own version, first seen Oct 13, 2026
- kucherenko/jscpdtheir own version, first seen Sep 2, 2026
- Abdox-menara/guard-skillstheir own version, first seen Aug 24, 2026
- anthropics/code-migration-kit-with-claude-codetheir own version, first seen Jul 8, 2026
Other versions5 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 5
- kucherenko/jscpd★ 6.5ksame name
- anthropics/code-migration-kit-with-claude-code★ 342same name
- ishandutta2007/Awesome-Claude-Skills★ 16same name
- tejastn10/workbench★ 1same name
- Abdox-menara/guard-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository5 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- ralph/CLAUDE.mdCLAUDE.md
- code-migrationSkill
- migration-comparisonSkill
- pf6-migrateSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
51 rules. 0 are common, 18 are distinctive, and 33 are very specific to this project.
0
Common
Most of the 4 edited versions we read keep these.
18
Distinctive
This author's own choices. Worth a look.
33
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 63
Look for how to run the application locally (e.g., npm start, npm run dev).
- Distinctive: line 79
Check whether the project is an OpenShift Console dynamic plugin.
- Very specific: line 84
If any indicator is present, mark the project as a console plugin (IS_CONSOLE_PLUGIN=true).
- Distinctive: line 91
Use custom rules? (If yes, get path)
- Very specific: line 134
5a. Prerequisites: Verify kubectl and curl are available.
- Distinctive: line 144
5b. Create kind cluster: Delete any existing cluster with the same name, then create one with a NodePort mapping (port 30443).
- Very specific: line 152
Wait for olm-operator, catalog-operator, and packageserver deployments to roll out.
- Very specific: line 154
5d. Deploy OpenShift Console via OLM: Create namespace openshift-console, an OperatorGroup, a ServiceAccount with cluster-admin, a …
- Very specific: line 161
Read cluster credentials for api_server and token
- Very specific: line 163
Check for console start script (ci/start-console.sh or console script in package.json)
- Very specific: line 174
If script exists: Before using it, read the script contents to determine how it starts the console bridge.
- Very specific: line 175
If the script runs the container without -d (foreground/blocking), the script itself will never exit.
- Very specific: line 176
If the script runs the container with -d (detached mode), the container starts in the background and the script exits on its own.
- Very specific: line 181
Every start-dev.sh script MUST follow this structure.
- Distinctive: line 264
If no script: use the same structure but replace step 3 with a generic podman console bridge:
- Distinctive: line 321
Run initial analysis to create the fix plan:
- Very specific: line 362
Run Kantra — Kantra analysis can take 5-15 minutes and will exceed the shell timeout.
- Very specific: line 376
Check $WORK_DIR/round-1/kantra/output.yaml exists before proceeding.
- Distinctive: line 381
Run build and lint commands
- Very specific: line 384
Create $WORK_DIR/status.md using the template below
- Very specific: line 429
Validate: Ensure the frontend analyzer provider is still running (kill -0 $(cat $WORK_DIR/frontend-provider.pid) 2>/dev/null — restart if …
- Distinctive: line 448
After each round, check:
- Distinctive: line 462
When an issue persists across 3+ rounds, analyze it:
- Distinctive: line 486
Run E2E/behavioral tests and complete target-specific validation.
- Distinctive: line 490
Run E2E test command discovered in Phase 1
- Distinctive: line 492
If tests FAIL → Fix issues, re-run
- Distinctive: line 493
If tests PASS → Continue to target validation
- Very specific: line 501
1. Load cluster credentials: Read $WORK_DIR/cluster-credentials.json (created in Phase 1).
- Distinctive: line 521
All must be checked:
- Very specific: line 546
Before generating the report, write the final ## Action Required section to status.md.
- Very specific: line 548
Read $WORK_DIR/visual-diff-report.md — check for any unchecked ([ ]) issues that remain after the visual fix loop
- Very specific: line 549
Read $WORK_DIR/visual-fixes.md — understand what visual issues were fixed and how
- Very specific: line 550
Remove any visual_review items from Action Required that were resolved by the visual-fix agent (i.e., the corresponding issues are now [x] …
- Distinctive: line 551
Add any new items discovered during visual fixing that need user attention (e.g., unfixable visual differences)
- Distinctive: line 564
If nothing requires review:
- Very specific: line 574
Read $WORK_DIR/status.md and extract:
- Very specific: line 584
Check for the latest Kantra output directory ($WORK_DIR/round-*/kantra/output.yaml).
- Very specific: line 586
If no Kantra output exists, set kantra_residual.total_incidents to 0.
- Very specific: line 590
If $WORK_DIR/visual-diff-report.md exists, extract per-page results (page name, status, notes).
- Very specific: line 594
Check for $WORK_DIR/baseline/ and $WORK_DIR/post-migration/ directories.
- Very specific: line 596
If neither directory exists, set visual.has_screenshots to false.
- Very specific: line 600
Create $WORK_DIR/report-data.json using the following schema:
- Very specific: line 690
If $WORK_DIR/visual-fixes.md exists, read it to understand what visual issues were fixed and how.
- Very specific: line 692
If a visual_review item in action_required refers to an issue that is now [x] in visual-diff-report.md, remove it from action_required — it …
- Very specific: line 693
If visual-diff-report.md has unchecked ([ ]) issues that are not represented in action_required, add them as visual_review items.
- Very specific: line 694
If visual-fixes.md documents fixes that contradict notes in action_required (e.g., an item says "not fixable" but visual-fixes.md shows it …
- Very specific: line 698
Before writing report-data.json, cross-check the data:
- Very specific: line 703
Every screenshot file referenced in visual.pages should exist in the baseline and post-migration directories
- Distinctive: line 728
Verify each fix: - don't break existing features
- Distinctive: line 729
Document unfixable issues: after 2+ failed approaches
- Distinctive: line 733
Attempt every group: Do not skip a group because it uses deprecated-but-functional APIs.
“Common” is measured: kept by at least 6 in 10 of the 4 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Code Migration
Migrate applications by identifying issues from multiple sources, fixing them systematically, and validating the result.
Issue Sources
Collect issues from ALL of these sources during analysis.
| Source | Examples |
| Kantra analysis | Deprecated APIs, breaking changes, migration patterns |
| Build errors | Compilation failures, type errors, missing deps |
| Lint errors | Style violations, unused imports |
| Test failures | Broken tests from API changes |
| Target docs | Breaking changes Kantra doesn't detect (check targets/<target>.md) |
Kantra is a static source code analysis tool that uses rules to identify migration issues in the source code.
Phase 1: Discovery
Step 1: Explore Project Structure
Discover build system, test commands, and lint configuration.
Find build system:
| File Found | Build Command |
package.json | npm run build or yarn build |
pom.xml | mvn compile or mvn package |
build.gradle / build.gradle.kts | ./gradlew build |
Makefile | make |
go.mod | go build ./... |
Cargo.toml | cargo build |
*.csproj / *.sln | dotnet build |
Find test commands:
| Test Type | How to Find |
| Unit tests | Check package.json scripts for test, test:unit; or mvn test, go test ./... |
| Integration | Look for test:integration, test:e2e scripts |
| E2E | Look for Cypress (cypress run), Playwright (npx playwright test), or similar |
Find lint command:
| Tool | Detection |
| ESLint | .eslintrc* file → npm run lint or npx eslint . |
| Prettier | .prettierrc* file → npx prettier --check . |
| Go | golangci-lint run |
| Python | flake8, pylint, ruff |
Find dev server command:
Look for how to run the application locally (e.g., npm start, npm run dev).
Record findings:
Build: <command>
Dev server: <command>
Lint: <command>
Unit tests: <command>
Integration tests: <command>
E2E tests: <command>
Primary language: <language>Step 2: Detect OpenShift Console Plugin
Check whether the project is an OpenShift Console dynamic plugin. Look for all of these indicators:
@openshift-console/dynamic-plugin-sdkinpackage.jsondependencies or devDependencies- A
console-extensions.jsonfile in the project root - A
ConsolePluginresource in any YAML/JSON file under the project
If any indicator is present, mark the project as a console plugin (IS_CONSOLE_PLUGIN=true). Record this in $WORK_DIR/status.md under a ## Project Type heading once the workspace is created.
Step 3: Build Kantra Command
Construct the Kantra analyze command flags.
Ask user:
- Use custom rules? (If yes, get path)
- Enable default rulesets?
Detect provider:
| Files Found | Provider |
*.java, pom.xml, build.gradle | java |
*.ts, *.tsx, package.json with TS deps | typescript |
*.js, *.jsx, package.json | javascript |
go.mod, *.go | go |
*.py, requirements.txt, pyproject.toml | python |
*.cs, *.csproj | dotnet |
Build flags:
# Base flags
--provider=<detected_provider>
# If custom rules provided:
--rules=<path_to_rules>
# If default rulesets enabled (and available for target):
--target=<migration_target>You add --input, --output, and --overwrite:
kantra analyze --input <project> --output $WORK_DIR/round-N/kantra --overwrite <FLAGS>Step 4: Create Workspace
WORK_DIR=$(mktemp -d -t migration-$(date +%m_%d_%y_%H))Step 5: Console Plugin Cluster Setup
Only perform this step if IS_CONSOLE_PLUGIN=true (detected in Step 2).
5a. Prerequisites: Verify kubectl and curl are available. If kind is not installed, install it:
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
[ "$ARCH" = "x86_64" ] && ARCH="amd64"
[ "$ARCH" = "aarch64" ] && ARCH="arm64"
curl -Lo ./kind "https://kind.sigs.k8s.io/dl/v0.27.0/kind-${OS}-${ARCH}"
chmod +x ./kind && mkdir -p "$HOME/.local/bin" && mv ./kind "$HOME/.local/bin/kind"5b. Create kind cluster: Delete any existing cluster with the same name, then create one with a NodePort mapping (port 30443). Wait for the node to be Ready.
5c. Install OLM:
kubectl create -f https://github.com/operator-framework/operator-lifecycle-manager/releases/latest/download/crds.yaml
kubectl wait --for=condition=Established crd --all --timeout=60s
kubectl create -f https://github.com/operator-framework/operator-lifecycle-manager/releases/latest/download/olm.yamlWait for olm-operator, catalog-operator, and packageserver deployments to roll out. Verify the operatorhubio-catalog CatalogSource is READY.
5d. Deploy OpenShift Console via OLM: Create namespace openshift-console, an OperatorGroup, a ServiceAccount with cluster-admin, a kubernetes.io/service-account-token Secret, a ClusterServiceVersion deploying quay.io/openshift/origin-console:latest with BRIDGE_K8S_MODE=off-cluster and BRIDGE_USER_AUTH=disabled, and a NodePort Service on port 30443. Wait for the CSV to reach Succeeded and the pod to be Ready.
5e. Verify console health: curl -sf -o /dev/null http://localhost:30443/health
5f. Collect credentials: Extract api_server, token, kubeconfig_path, ca_cert_path, context_name, and console_url. Save to $WORK_DIR/cluster-credentials.json.
5g. Determine console plugin dev command:
- Read cluster credentials for
api_serverandtoken - Detect plugin name from
package.jsonnamefield - Check for console start script (
ci/start-console.shorconsolescript inpackage.json)
CRITICAL — npm start and webpack dev servers are long-running processes that NEVER exit on their own.
They start an HTTP server and keep running indefinitely to serve requests. If you run them without &, your session WILL hang indefinitely and never recover.
You MUST construct the dev command as a self-contained shell script ($WORK_DIR/start-dev.sh) that handles cleanup, backgrounding, PID tracking, logging, and readiness polling internally. Never run npm start or dev server commands directly in the shell tool — always go through the script.
Also create a companion $WORK_DIR/stop-dev.sh script for clean shutdown.
Do not run the dev command here to test it. Just construct and save it. It will be executed later with proper background management and readiness polling.
- If script exists: Before using it, read the script contents to determine how it starts the console bridge. Check whether the script runs
podman runordocker runwith or without the-d(detach) flag:- If the script runs the container without
-d(foreground/blocking), the script itself will never exit. You must run the script in the background with&and capture its PID. - If the script runs the container with
-d(detached mode), the container starts in the background and the script exits on its own. Do not background the script with&— let it run to completion so any startup errors are reported. - If you cannot determine the behavior from reading the script, default to running it in the background with
&to avoid hanging the session.
- If the script runs the container without
start-dev.sh Template
Every start-dev.sh script MUST follow this structure. The script handles its own backgrounding — callers run it with bash $WORK_DIR/start-dev.sh (no & needed).
Required elements:
- Port cleanup — kill any leftover processes on ports 9001 and 9000 from previous failed runs
nohup+ log redirection — prevent shell timeout from killing the process; write output to log files- PID files — write PIDs to
$WORK_DIR/*.pidfor clean shutdown - Readiness polling — poll each port with
curl, accepting exit code 22 (HTTP 404) for port 9001 - Exit immediately — the script must exit after starting and verifying servers, not block
Example (blocking console script — run with &):
#!/bin/bash
WORK_DIR=<work_dir>
# Cleanup: kill leftover processes from previous runs
fuser -k 9001/tcp 2>/dev/null || true
fuser -k 9000/tcp 2>/dev/null || true
podman stop migration-console okd-console 2>/dev/null || docker stop migration-console okd-console 2>/dev/null || true
sleep 1
# 1. Start webpack dev server in background
cd <project_path>
nohup npm start > "$WORK_DIR/webpack.log" 2>&1 &
echo $! > "$WORK_DIR/webpack.pid"
# 2. Poll until webpack dev server is ready on port 9001 (up to 120s)
# Accept exit code 0 (HTTP 200) or 22 (HTTP 404 — "Cannot GET /") as "ready"
for i in $(seq 1 60); do
curl -sf -o /dev/null http://localhost:9005 2>/dev/null; rc=$?
[ $rc -eq 0 ] || [ $rc -eq 22 ] && break
sleep 2
done
# 3. Start console bridge in background (blocking script)
BRIDGE_K8S_MODE_OFF_CLUSTER_ENDPOINT=<api_server> \
BRIDGE_K8S_AUTH_BEARER_TOKEN=<token> \
nohup bash ./ci/start-console.sh > "$WORK_DIR/bridge.log" 2>&1 &
echo $! > "$WORK_DIR/bridge.pid"
# 4. Poll until console bridge is ready on port 9000 (up to 120s)
for i in $(seq 1 60); do
curl -sf -o /dev/null http://localhost:9000 2>/dev/null && break
sleep 2
done
echo "Dev servers ready"Example (detached console script — runs podman run -d and exits):
#!/bin/bash
WORK_DIR=<work_dir>
# Cleanup: kill leftover processes from previous runs
fuser -k 9001/tcp 2>/dev/null || true
fuser -k 9000/tcp 2>/dev/null || true
podman stop migration-console okd-console 2>/dev/null || docker stop migration-console okd-console 2>/dev/null || true
sleep 1
# 1. Start webpack dev server in background
cd <project_path>
nohup npm start > "$WORK_DIR/webpack.log" 2>&1 &
echo $! > "$WORK_DIR/webpack.pid"
# 2. Poll until webpack dev server is ready on port 9001 (up to 120s)
for i in $(seq 1 60); do
curl -sf -o /dev/null http://localhost:9005 2>/dev/null; rc=$?
[ $rc -eq 0 ] || [ $rc -eq 22 ] && break
sleep 2
done
# 3. Run console bridge script (starts container with -d, exits on its own)
BRIDGE_K8S_MODE_OFF_CLUSTER_ENDPOINT=<api_server> \
BRIDGE_K8S_AUTH_BEARER_TOKEN=<token> \
bash ./ci/start-console.sh > "$WORK_DIR/bridge.log" 2>&1
# 4. Poll until console bridge is ready on port 9000 (up to 120s)
for i in $(seq 1 60); do
curl -sf -o /dev/null http://localhost:9000 2>/dev/null && break
sleep 2
done
echo "Dev servers ready"- If no script: use the same structure but replace step 3 with a generic podman console bridge:
# 3. Start console bridge in background (also long-running) podman run --rm --name=migration-console --network=host \ -e BRIDGE_USER_AUTH=disabled -e BRIDGE_K8S_MODE=off-cluster \ -e BRIDGE_K8S_MODE_OFF_CLUSTER_ENDPOINT=<api_server> \ -e BRIDGE_K8S_MODE_OFF_CLUSTER_SKIP_VERIFY_TLS=true \ -e BRIDGE_K8S_AUTH=bearer-token -e BRIDGE_K8S_AUTH_BEARER_TOKEN=<token> \ -e BRIDGE_PLUGINS=<plugin_name>=http://localhost:9005 \ -e BRIDGE_LISTEN=http://0.0.0.0:9000 \ quay.io/openshift/origin-console:latest > "$WORK_DIR/bridge.log" 2>&1 & echo $! > "$WORK_DIR/bridge.pid"
stop-dev.sh
Also create $WORK_DIR/stop-dev.sh:
#!/bin/bash
WORK_DIR=<work_dir>
kill $(cat "$WORK_DIR/webpack.pid" 2>/dev/null) 2>/dev/null || true
kill $(cat "$WORK_DIR/bridge.pid" 2>/dev/null) 2>/dev/null || true
podman stop migration-console okd-console 2>/dev/null || docker stop migration-console okd-console 2>/dev/null || true
fuser -k 9001/tcp 2>/dev/null || true
fuser -k 9000/tcp 2>/dev/null || true
rm -f "$WORK_DIR/webpack.pid" "$WORK_DIR/bridge.pid"- Both servers must be running: webpack dev server on port 9001 (serves plugin JS) and console bridge on port 9000 (provides the HTML shell that loads the plugin)
- Console dev URL is
http://localhost:9000 - Save the console dev command as a shell script file at
$WORK_DIR/start-dev.sh(not as a JSON string). Also save the URL to$WORK_DIR/console-dev-setup.jsonwith a reference to the script path.
Step 6: Check Target Technology Specific Guidance
Look for a target-specific file in targets/. Match by lowercased target name without version numbers (e.g., "PatternFly 6" → patternfly.md, "Spring Boot 3.x" → spring-boot.md). Follow ALL pre-migration steps in the target file sequentially — each step must complete before the next one starts. Do not proceed to Phase 2 until all pre-migration steps are complete.
Phase 2: Fix Loop
Establish Test Baseline
Before making any code changes, run the full test suite to record pre-existing failures. Record results in $WORK_DIR/status.md:
## Test Baseline
- Total: [count]
- Passing: [count]
- Pre-existing failures:
- [test name]: [brief reason] (NOT migration-related)Pre-existing failures do not count against the exit criteria. The exit check is: "no NEW test failures introduced by the migration." Compare test results against this baseline, not against zero failures.
First Round Only
Run initial analysis to create the fix plan:
- Start the frontend analyzer provider — this must be running before Kantra is invoked:
Replace# Create provider settings file cat > "$WORK_DIR/provider_settings.json" <<'PROVIDER_EOF' [ { "name": "frontend", "address": "localhost:9005", "initConfig": [ { "analysisMode": "source-only", "location": "<project>" } ] }, { "name": "builtin", "initConfig": [ { "location": "<project>" } ] } ] PROVIDER_EOF # Start frontend analyzer provider in background nohup frontend-analyzer-provider serve -p 9005 > "$WORK_DIR/frontend-provider.log" 2>&1 & echo $! > "$WORK_DIR/frontend-provider.pid" # Wait for provider to be ready for i in $(seq 1 30); do curl -sf -o /dev/null http://localhost:9005 2>/dev/null; rc=$? [ $rc -eq 0 ] || [ $rc -eq 22 ] && break sleep 1 done<project>with the actual project path in the provider settings file. - Run Kantra — Kantra analysis can take 5-15 minutes and will exceed the shell timeout. Always run it in the background with
nohupand redirect output to a log file. Important: Kantra fails if the current directory is the input path. Alwayscd $WORK_DIRbefore running. Also setJAVA_HOMEif not already set:
Poll for completion:cd "$WORK_DIR" export JAVA_HOME="${JAVA_HOME:-$(dirname $(dirname $(readlink -f $(which java))))}" nohup kantra analyze --input <project> --output $WORK_DIR/round-1/kantra --overwrite \ --override-provider-settings "$WORK_DIR/provider_settings.json" \ --enable-default-rulesets=false --skip-static-report --no-dependency-rules \ --mode source-only --run-local=true --provider=java \ --label-selector '!impact=frontend-testing' \ <FLAGS> > $WORK_DIR/round-1/kantra-run.log 2>&1 & KANTRA_PID=$!while kill -0 $KANTRA_PID 2>/dev/null; do sleep 10; doneCheck$WORK_DIR/round-1/kantra/output.yamlexists before proceeding. If Kantra failed, check$WORK_DIR/round-1/kantra-run.logfor errors. Note: The<FLAGS>placeholder is replaced with flags from the kantra command builder. The flags above (--override-provider-settings,--enable-default-rulesets=false, etc.) are always included. If the command builder returns any of these same flags, do not duplicate them. - Parse Kantra output using the helper script:
- Overview:
python3 scripts/kantra_output_helper.py analyze $WORK_DIR/round-1/kantra/output.yaml - File details:
python3 scripts/kantra_output_helper.py file $WORK_DIR/round-1/kantra/output.yaml <file>
- Overview:
- Run build and lint commands
- Run unit tests
- Collect ALL issues from ALL sources (see Issue Sources table)
- Create
$WORK_DIR/status.mdusing the template below
Fix Loop Template
Create $WORK_DIR/status.md:
# Migration Status
## Groups
- [ ] Group 1: [Name] - [Brief description]
- [ ] Group 2: [Name] - [Brief description]
- [ ] Group 3: [Name] - [Brief description]
## Group Details
### Group 1: [Name]
**Why grouped**: [Related issues, same subsystem, etc.]
**Issues**:
- [Issue from Kantra/build/lint/tests]
- [Issue from Kantra/build/lint/tests]
**Files**: [file1.ts, file2.ts]
### Group 2: [Name]
...
## Round Log
(Append after each round)Each Round
Round Checklist:
- [ ] Pick next incomplete group
- [ ] Apply fixes for that group
- [ ] Run Kantra + build + lint + unit tests
- [ ] Mark group complete in status.md
- [ ] Add new issues to plan if any appeared- Pick: Select first incomplete group from status.md
- Fix: Apply all fixes for that group. Before renaming any prop or API based on Kantra suggestions, verify the new name exists in the target framework's type definitions (e.g., check the
.d.tsfiles or runtsc --noEmit). Kantra rules may suggest renames that are not yet reflected in the installed version's types — applying them blindly will break the build. - Validate: Ensure the frontend analyzer provider is still running (
kill -0 $(cat $WORK_DIR/frontend-provider.pid) 2>/dev/null— restart if needed). Run Kantra (in background withnohupas described above, including all--override-provider-settingsand related flags), build, lint, unit tests - Update: Mark group done, log the round
Run tests concisely:
- Capture output, report only failures
- Format:
PASS: X tests, FAIL: Y tests - For failures, show test name and error message only
Append to status.md:
### Round N: [Group Name]
- Fixed: [count] issues
- New issues: [count or "none"]
- Build: PASS/FAIL
- Tests: PASS/FAIL/NONEExit Check
After each round, check:
| Condition | Done? |
| All groups complete | ☐ |
| Kantra: 0 real issues (false positives documented in status.md) | ☐ |
| Build: passes | ☐ |
| Unit tests: no new failures vs baseline | ☐ |
- Any unchecked → Continue loop (next group)
- All checked → Proceed to Phase 3
If Stuck (Same Issue 3+ Rounds)
When an issue persists across 3+ rounds, analyze it:
Run persistent issues script:
python3 scripts/persistent_issues_analyzer.py $WORK_DIRFor each persistent issue, determine:
| Question | Check |
| False positive? | Rule too strict? Pattern actually valid? |
| Fixable? | Multiple approaches failed? Needs manual decision? |
| Blocking factor? | External deps? Domain knowledge needed? |
Categorize:
- Fix: Real issue, try different approach
- Ignore: False positive, document why in status.md
- Document: Real but needs manual intervention, add to status.md
Phase 3: Final Validation
Run E2E/behavioral tests and complete target-specific validation.
E2E Testing
- Run E2E test command discovered in Phase 1
- Report results concisely (pass/fail counts, failure details)
- If tests FAIL → Fix issues, re-run
- If tests PASS → Continue to target validation
Console Plugin Cluster Validation
Only perform this section if IS_CONSOLE_PLUGIN=true (detected in Phase 1 step 2).
Console dynamic plugins must be tested inside an OpenShift Console to verify they load, register their extensions, and render correctly.
1. Load cluster credentials: Read $WORK_DIR/cluster-credentials.json (created in Phase 1). Verify cluster is still running by checking connectivity to the api_server. If not responsive, re-provision the cluster (repeat Phase 1 steps 5a-5f) and update $WORK_DIR/cluster-credentials.json.
2. Build and deploy plugin: Build the plugin container image, load it into kind, create a Deployment + Service + ConsolePlugin CR in the cluster. Use existing deployment manifests if available.
3. Verify plugin: Confirm the plugin appears in the console and check logs for loading errors.
Log the result in $WORK_DIR/status.md:
### Cluster Validation
- Console plugin deployed: YES/NO
- Plugin loaded in console: YES/NO
- Console URL: <url>Target Validation
Follow all post-migration steps in targets/<target>.md. These steps are mandatory — do not skip them. The migration is not complete until all post-migration validation passes.
Exit Criteria
All must be checked:
- Kantra: 0 real issues (false positives documented)
- Build: passes
- Unit tests: no new failures vs baseline
- E2E tests: pass (or no new failures vs baseline)
- Target-specific validation complete
- Console plugin loads in cluster (if
IS_CONSOLE_PLUGIN=true)
Update status.md:
## Complete
- Total rounds: N
- Build: PASS
- Unit tests: PASS
- E2E tests: PASS
- Target validation: PASS
- Console plugin validation: PASS/SKIPPhase 4: Report
Before generating the report, write the final ## Action Required section to status.md. This must reflect the end state of the migration, including visual fixes.
- Read
$WORK_DIR/visual-diff-report.md— check for any unchecked ([ ]) issues that remain after the visual fix loop - Read
$WORK_DIR/visual-fixes.md— understand what visual issues were fixed and how - Remove any
visual_reviewitems from Action Required that were resolved by the visual-fix agent (i.e., the corresponding issues are now[x]in the diff report) - Add any new items discovered during visual fixing that need user attention (e.g., unfixable visual differences)
Append the final ## Action Required section to status.md listing every item the user should still review. Use bullet format with type prefix:
## Action Required
- **Unresolved Issue**: [description] → [recommendation]
- **False Positive**: [description] → [recommendation]
- **Visual Review** (page: [name]): [description] → [recommendation]
- **Manual Intervention**: [description] → [recommendation]If nothing requires review:
## Action Required
None1. Read status.md
Read $WORK_DIR/status.md and extract:
- Complete section: total rounds, build/test/validation status
- Groups: list of groups with their completion status
- Group Details: for each group — name, description, files, issues
- Round Log: each round's fixed count, new issues, build/test results
- Action Required section: items the user must review (unresolved issues, false positives, visual reviews, manual interventions)
2. Read Kantra Assessment
Check for the latest Kantra output directory ($WORK_DIR/round-*/kantra/output.yaml). If a final round exists, note any residual incidents (rule, count, reason for keeping).
If no Kantra output exists, set kantra_residual.total_incidents to 0.
3. Read Visual Comparison Report
If $WORK_DIR/visual-diff-report.md exists, extract per-page results (page name, status, notes). Map unchecked items ([ ]) to fail status and checked items ([x]) to pass.
4. List Screenshot Directories
Check for $WORK_DIR/baseline/ and $WORK_DIR/post-migration/ directories. List filenames in each to populate the visual pages array.
If neither directory exists, set visual.has_screenshots to false.
5. Build report-data.json
Create $WORK_DIR/report-data.json using the following schema:
{
"migration": {
"source": "string",
"target": "string",
"project": "string (project path)",
"timestamp": "ISO 8601",
"workspace": "string (workspace path)"
},
"summary": {
"total_rounds": "number",
"status": "complete|incomplete",
"build": "PASS|FAIL",
"unit_tests": "PASS|FAIL|NONE",
"e2e_tests": "PASS|FAIL|NONE",
"lint": "PASS|FAIL|NONE",
"target_validation": "PASS|FAIL|NONE"
},
"action_required": [
{
"type": "unresolved_issue|false_positive|visual_review|manual_intervention",
"description": "string",
"recommendation": "string (optional)",
"details": "string (optional)",
"page": "string (optional, for visual_review)"
}
],
"groups": [
{
"name": "string",
"status": "complete|incomplete",
"issues_fixed": "number",
"files": ["string"],
"description": "string"
}
],
"rounds": [
{
"number": "number",
"group": "string",
"issues_fixed": "number",
"new_issues": "number",
"build": "PASS|FAIL",
"tests": "string (e.g. '265/265' or '225/262 (37 snapshot mismatches)')"
}
],
"visual": {
"has_screenshots": "boolean",
"baseline_dir": "string (relative to work_dir)",
"post_migration_dir": "string (relative to work_dir)",
"pages": [
{
"name": "string",
"baseline": "string (filename, e.g. 'login.png')",
"post_migration": "string (filename, e.g. 'login.png')",
"status": "pass|fail|info",
"notes": "string"
}
]
},
"kantra_residual": {
"total_incidents": "number",
"categories": [
{
"rule": "string",
"count": "number",
"reason": "string"
}
]
}
}Field population rules:
migration.source/migration.target: from the source and target technologiesmigration.project: the project pathmigration.timestamp: current time in ISO 8601migration.workspace:$WORK_DIRsummary: from the Complete section of status.mdaction_required: from the Action Required section of status.md. Parse each bullet into type, description, and recommendation. If "None", use an empty array.groups: from Groups and Group Details sections. Mark[x]groups as "complete",[ ]as "incomplete". Count issues from Group Details. Extract files list.rounds: from Round Log entries. Parse fixed count, new issues count, build and test results.visual: from screenshot directories and visual-diff-report.md. If no screenshots exist, sethas_screenshotsto false and omit pages.kantra_residual: from the latest Kantra output. If 0 residual issues, settotal_incidentsto 0 andcategoriesto empty array.
6. Read Visual Fixes
If $WORK_DIR/visual-fixes.md exists, read it to understand what visual issues were fixed and how. Use this to verify consistency of action_required:
- If a
visual_reviewitem inaction_requiredrefers to an issue that is now[x]invisual-diff-report.md, remove it fromaction_required— it was fixed. - If
visual-diff-report.mdhas unchecked ([ ]) issues that are not represented inaction_required, add them asvisual_reviewitems. - If
visual-fixes.mddocuments fixes that contradict notes inaction_required(e.g., an item says "not fixable" butvisual-fixes.mdshows it was fixed), update accordingly.
7. Verify Consistency
Before writing report-data.json, cross-check the data:
summary.statusshould becompleteonly if all groups are complete, build passes, and tests passaction_requiredshould not contain items that are contradicted by other artifacts (e.g., visual issues marked as needing review but already[x]in the diff report)visual.pagesstatus values should matchvisual-diff-report.md—[x]→pass,[ ]→fail- Every screenshot file referenced in
visual.pagesshould exist in the baseline and post-migration directories
8. Stop Frontend Analyzer Provider
Stop the frontend analyzer provider — it is no longer needed after analysis is complete:
kill $(cat "$WORK_DIR/frontend-provider.pid" 2>/dev/null) 2>/dev/null || true
rm -f "$WORK_DIR/frontend-provider.pid"9. Generate HTML Report
Run:
python3 scripts/generate_migration_report.py $WORK_DIRTell the user the path to the generated report.html.
Guidelines
- One group per round for clear feedback
- Follow planned order - foundation before dependent changes
- Verify each fix - don't break existing features
- Document unfixable issues after 2+ failed approaches
- Use all issue sources - Kantra is just one input
- Report test results concisely - counts and failures only, not full output
- Never renumber, relabel, or remove groups from status.md. Groups are numbered when the plan is created and those numbers are permanent. If a group cannot be fixed after 2+ attempts, mark it as
[!] Group N: [Name] - UNFIXABLE: [reason]— do not delete it, reassign its number to another group, or merge it into a different group. - Attempt every group. Do not skip a group because it uses deprecated-but-functional APIs. If Kantra flags it, attempt the migration to the new API. Only classify a group as unfixable if the fix breaks the build or tests after 2+ different approaches.
- NEVER run dev servers directly in the shell tool —
npm start,webpack serve,npm run dev, and similar dev server commands are long-running and will hang the shell. Always use$WORK_DIR/start-dev.shto start them and$WORK_DIR/stop-dev.shto stop them. These scripts handle backgrounding, PID tracking, log redirection, port cleanup, and readiness polling internally. Never construct dev server commands inline. - Before starting dev servers, always clean up leftover processes — run
bash $WORK_DIR/stop-dev.shfirst, or at minimumfuser -k 9001/tcp 2>/dev/null; fuser -k 9000/tcp 2>/dev/nullto avoidEADDRINUSEerrors from previous failed runs.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

