agentleFS
konveyor-ecosystemkonveyor-ecosystem/playpen-pf-mig-skills/skills/code-migration-inline/SKILL.md

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

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
  1. Very specific:

    Look for how to run the application locally (e.g., npm start, npm run dev).

    line 63
  2. Distinctive:

    Check whether the project is an OpenShift Console dynamic plugin.

    line 79
  3. Very specific:

    If any indicator is present, mark the project as a console plugin (IS_CONSOLE_PLUGIN=true).

    line 84
  4. Distinctive:

    Use custom rules? (If yes, get path)

    line 91
  5. Very specific:

    5a. Prerequisites: Verify kubectl and curl are available.

    line 134
  6. Distinctive:

    5b. Create kind cluster: Delete any existing cluster with the same name, then create one with a NodePort mapping (port 30443).

    line 144
  7. Very specific:

    Wait for olm-operator, catalog-operator, and packageserver deployments to roll out.

    line 152
  8. Very specific:

    5d. Deploy OpenShift Console via OLM: Create namespace openshift-console, an OperatorGroup, a ServiceAccount with cluster-admin, a …

    line 154
  9. Very specific:

    Read cluster credentials for api_server and token

    line 161
  10. Very specific:

    Check for console start script (ci/start-console.sh or console script in package.json)

    line 163
  11. Very specific:

    If script exists: Before using it, read the script contents to determine how it starts the console bridge.

    line 174
  12. Very specific:

    If the script runs the container without -d (foreground/blocking), the script itself will never exit.

    line 175
  13. Very specific:

    If the script runs the container with -d (detached mode), the container starts in the background and the script exits on its own.

    line 176
  14. Very specific:

    Every start-dev.sh script MUST follow this structure.

    line 181
  15. Distinctive:

    If no script: use the same structure but replace step 3 with a generic podman console bridge:

    line 264
  16. Distinctive:

    Run initial analysis to create the fix plan:

    line 321
  17. Very specific:

    Run Kantra — Kantra analysis can take 5-15 minutes and will exceed the shell timeout.

    line 362
  18. Very specific:

    Check $WORK_DIR/round-1/kantra/output.yaml exists before proceeding.

    line 376
  19. Distinctive:

    Run build and lint commands

    line 381
  20. Very specific:

    Create $WORK_DIR/status.md using the template below

    line 384
  21. Very specific:

    Validate: Ensure the frontend analyzer provider is still running (kill -0 $(cat $WORK_DIR/frontend-provider.pid) 2>/dev/null — restart if …

    line 429
  22. Distinctive:

    After each round, check:

    line 448
  23. Distinctive:

    When an issue persists across 3+ rounds, analyze it:

    line 462
  24. Distinctive:

    Run E2E/behavioral tests and complete target-specific validation.

    line 486
  25. Distinctive:

    Run E2E test command discovered in Phase 1

    line 490
  26. Distinctive:

    If tests FAIL → Fix issues, re-run

    line 492
  27. Distinctive:

    If tests PASS → Continue to target validation

    line 493
  28. Very specific:

    1. Load cluster credentials: Read $WORK_DIR/cluster-credentials.json (created in Phase 1).

    line 501
  29. Distinctive:

    All must be checked:

    line 521
  30. Very specific:

    Before generating the report, write the final ## Action Required section to status.md.

    line 546
  31. Very specific:

    Read $WORK_DIR/visual-diff-report.md — check for any unchecked ([ ]) issues that remain after the visual fix loop

    line 548
  32. Very specific:

    Read $WORK_DIR/visual-fixes.md — understand what visual issues were fixed and how

    line 549
  33. Very specific:

    Remove any visual_review items from Action Required that were resolved by the visual-fix agent (i.e., the corresponding issues are now [x] …

    line 550
  34. Distinctive:

    Add any new items discovered during visual fixing that need user attention (e.g., unfixable visual differences)

    line 551
  35. Distinctive:

    If nothing requires review:

    line 564
  36. Very specific:

    Read $WORK_DIR/status.md and extract:

    line 574
  37. Very specific:

    Check for the latest Kantra output directory ($WORK_DIR/round-*/kantra/output.yaml).

    line 584
  38. Very specific:

    If no Kantra output exists, set kantra_residual.total_incidents to 0.

    line 586
  39. Very specific:

    If $WORK_DIR/visual-diff-report.md exists, extract per-page results (page name, status, notes).

    line 590
  40. Very specific:

    Check for $WORK_DIR/baseline/ and $WORK_DIR/post-migration/ directories.

    line 594
  41. Very specific:

    If neither directory exists, set visual.has_screenshots to false.

    line 596
  42. Very specific:

    Create $WORK_DIR/report-data.json using the following schema:

    line 600
  43. Very specific:

    If $WORK_DIR/visual-fixes.md exists, read it to understand what visual issues were fixed and how.

    line 690
  44. Very specific:

    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 …

    line 692
  45. Very specific:

    If visual-diff-report.md has unchecked ([ ]) issues that are not represented in action_required, add them as visual_review items.

    line 693
  46. Very specific:

    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 …

    line 694
  47. Very specific:

    Before writing report-data.json, cross-check the data:

    line 698
  48. Very specific:

    Every screenshot file referenced in visual.pages should exist in the baseline and post-migration directories

    line 703
  49. Distinctive:

    Verify each fix: - don't break existing features

    line 728
  50. Distinctive:

    Document unfixable issues: after 2+ failed approaches

    line 729
  51. Distinctive:

    Attempt every group: Do not skip a group because it uses deprecated-but-functional APIs.

    line 733

“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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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.

SourceExamples
Kantra analysisDeprecated APIs, breaking changes, migration patterns
Build errorsCompilation failures, type errors, missing deps
Lint errorsStyle violations, unused imports
Test failuresBroken tests from API changes
Target docsBreaking 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 FoundBuild Command
package.jsonnpm run build or yarn build
pom.xmlmvn compile or mvn package
build.gradle / build.gradle.kts./gradlew build
Makefilemake
go.modgo build ./...
Cargo.tomlcargo build
*.csproj / *.slndotnet build

Find test commands:

Test TypeHow to Find
Unit testsCheck package.json scripts for test, test:unit; or mvn test, go test ./...
IntegrationLook for test:integration, test:e2e scripts
E2ELook for Cypress (cypress run), Playwright (npx playwright test), or similar

Find lint command:

ToolDetection
ESLint.eslintrc* file → npm run lint or npx eslint .
Prettier.prettierrc* file → npx prettier --check .
Gogolangci-lint run
Pythonflake8, 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-sdk in package.json dependencies or devDependencies
  • A console-extensions.json file in the project root
  • A ConsolePlugin resource 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 FoundProvider
*.java, pom.xml, build.gradlejava
*.ts, *.tsx, package.json with TS depstypescript
*.js, *.jsx, package.jsonjavascript
go.mod, *.gogo
*.py, requirements.txt, pyproject.tomlpython
*.cs, *.csprojdotnet

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.yaml

Wait 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_server and token
  • Detect plugin name from package.json name field
  • Check for console start script (ci/start-console.sh or console script in package.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 run or docker run with 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.

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:

  1. Port cleanup — kill any leftover processes on ports 9001 and 9000 from previous failed runs
  2. nohup + log redirection — prevent shell timeout from killing the process; write output to log files
  3. PID files — write PIDs to $WORK_DIR/*.pid for clean shutdown
  4. Readiness polling — poll each port with curl, accepting exit code 22 (HTTP 404) for port 9001
  5. 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.json with 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:

  1. Start the frontend analyzer provider — this must be running before Kantra is invoked:
    # 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
    Replace <project> with the actual project path in the provider settings file.
  2. Run Kantra — Kantra analysis can take 5-15 minutes and will exceed the shell timeout. Always run it in the background with nohup and redirect output to a log file. Important: Kantra fails if the current directory is the input path. Always cd $WORK_DIR before running. Also set JAVA_HOME if not already set:
    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=$!
    Poll for completion: while kill -0 $KANTRA_PID 2>/dev/null; do sleep 10; done Check $WORK_DIR/round-1/kantra/output.yaml exists before proceeding. If Kantra failed, check $WORK_DIR/round-1/kantra-run.log for 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.
  3. 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>
  4. Run build and lint commands
  5. Run unit tests
  6. Collect ALL issues from ALL sources (see Issue Sources table)
  7. Create $WORK_DIR/status.md using 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
  1. Pick: Select first incomplete group from status.md
  2. 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.ts files or run tsc --noEmit). Kantra rules may suggest renames that are not yet reflected in the installed version's types — applying them blindly will break the build.
  3. 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 with nohup as described above, including all --override-provider-settings and related flags), build, lint, unit tests
  4. 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/NONE

Exit Check

After each round, check:

ConditionDone?
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_DIR

For each persistent issue, determine:

QuestionCheck
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

  1. Run E2E test command discovered in Phase 1
  2. Report results concisely (pass/fail counts, failure details)
  3. If tests FAIL → Fix issues, re-run
  4. 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/SKIP

Phase 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.

  1. Read $WORK_DIR/visual-diff-report.md — check for any unchecked ([ ]) issues that remain after the visual fix loop
  2. Read $WORK_DIR/visual-fixes.md — understand what visual issues were fixed and how
  3. Remove any visual_review items from Action Required that were resolved by the visual-fix agent (i.e., the corresponding issues are now [x] in the diff report)
  4. 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

None

1. 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 technologies
  • migration.project: the project path
  • migration.timestamp: current time in ISO 8601
  • migration.workspace: $WORK_DIR
  • summary: from the Complete section of status.md
  • action_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, set has_screenshots to false and omit pages.
  • kantra_residual: from the latest Kantra output. If 0 residual issues, set total_incidents to 0 and categories to 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_review item in action_required refers to an issue that is now [x] in visual-diff-report.md, remove it from action_required — it was fixed.
  • If visual-diff-report.md has unchecked ([ ]) issues that are not represented in action_required, add them as visual_review items.
  • 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 was fixed), update accordingly.

7. Verify Consistency

Before writing report-data.json, cross-check the data:

  • summary.status should be complete only if all groups are complete, build passes, and tests pass
  • action_required should 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.pages status values should match visual-diff-report.md — [x] → pass, [ ] → fail
  • Every screenshot file referenced in visual.pages should 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_DIR

Tell 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.sh to start them and $WORK_DIR/stop-dev.sh to 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.sh first, or at minimum fuser -k 9001/tcp 2>/dev/null; fuser -k 9000/tcp 2>/dev/null to avoid EADDRINUSE errors from previous failed runs.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.