migrate-to-svelte

star 624

Orchestrate parallel migration of Blade React components to Svelte 5. Sets up isolated git worktrees, runs Plan/Execute/Verify agents in parallel, and opens one PR per component. Use when user says "migrate <component> to svelte", "/migrate-to-svelte", or similar.

razorpay By razorpay schedule Updated 6/3/2026

name: migrate-to-svelte description: > Orchestrate parallel migration of Blade React components to Svelte 5. Sets up isolated git worktrees, runs Plan/Execute/Verify agents in parallel, and opens one PR per component. Use when user says "migrate to svelte", "/migrate-to-svelte", or similar.

Orchestrator — Parallel Migration Pipeline Controller

Main entry point for component migration. Parses input, sets up isolated git worktrees per component, runs Plan/Execute/Verify in parallel where safe, manages per-component human gates, and opens one PR per component.

⚠️ PRE-FLIGHT CHECKLIST — Acknowledge Before Starting

Before you take ANY action, confirm you understand:

  • I will spawn agents via the Agent tool (subagent_type: plan-svelte-migration | execute-svelte-migration | verify-svelte-migration).
  • I will NOT read .claude/agents/*.md myself — those files are loaded by the Agent tool when the matching subagent_type is invoked.
  • I will pass each component's worktree absolute path in the Agent prompt so the agent can scope all its file/shell operations to that worktree.
  • To run agents in parallel for a phase, I will send one message containing multiple Agent tool calls (one per component). All N agents execute concurrently and the orchestrator only proceeds once they all return.

Your ONLY Valid Action Patterns:

# Spawn an agent in the worktree (parent waits for all parallel calls in the
# same message to complete):
Agent(
  subagent_type: "plan-svelte-migration" | "execute-svelte-migration" | "verify-svelte-migration",
  description: "<short, distinct title>",
  prompt: "<see prompt templates in Steps 1, 4, 5>",
  run_in_background: false,
)
# Worktree management (Bash tool):
git worktree add -B feat/blade-svelte/{Name} .claude/worktrees/{Name} origin/master
git worktree remove .claude/worktrees/{Name}
# Per-component PR creation (Bash tool, run inside the worktree):
cd .claude/worktrees/{Name} && git push -u origin feat/blade-svelte/{Name}
cd .claude/worktrees/{Name} && gh pr create --base master --title "..." --body "..."

⚠️ Your Role

You are the orchestrator. You coordinate the migration pipeline by spawning specialized agents in parallel inside isolated git worktrees. You do NOT execute any phase work yourself.

Your ONLY job:

  1. Set up one git worktree + one branch per component.
  2. Spawn Plan/Execute/Verify agents in parallel via the Agent tool, passing each component's worktree absolute path in the prompt.
  3. Read the artifacts each agent produces.
  4. Present artifacts to the user at human gates.
  5. Open one PR per migrated component.
  6. Maintain .claude/artifacts/batch-status.md as the live source of truth.

Include

Use the Read tool to load these files before starting:

  1. .claude/rules/orchestrator-guardrails.md

Input

  • Component name(s): single name or comma-separated list (e.g., Alert or Alert, Card, Checkbox).

Output

  • One git worktree per component at .claude/worktrees/{Name}/.
  • One branch per component: feat/blade-svelte/{Name} (based on origin/master).
  • One PR per component (base = master, title = feat(blade-svelte): {Name} component).
  • Per-component artifacts at .claude/worktrees/{Name}/.claude/artifacts/{Name}/.
  • Live batch tracker at .claude/artifacts/batch-status.md (in the main checkout, not in any worktree).

Step 0: Setup — Worktrees, Branches, Port Allocation

0.1 Parse and validate input

  1. Parse component name(s) from user input. Trim whitespace, dedupe.
  2. For each component, verify packages/blade/src/components/{Name}/ exists in the main checkout. If missing → drop from batch and warn user.
  3. Check packages/blade-svelte/src/components/index.ts in the main checkout. If {Name} is already exported → drop from batch with "already migrated".
  4. Capture the main checkout absolute path (pwd) once. You will use it to construct worktree absolute paths for the Agent prompts.

0.2 Allocate slots and ports

Number the surviving components 1..N (in input order).

  • React Storybook port (shared, all slots): 9009 (the script's default).
  • Svelte Storybook port (per slot i): 6000 + i*100 + 7 → slot 1 = 6107, slot 2 = 6207, …, slot 5 = 6507. The +7 matches blade-svelte's default Storybook port (6007); i*100 offsets each parallel slot to avoid collisions.

The pipeline owns 9009 for the duration of the batch — if you need to run yarn react:storybook in your main checkout concurrently, pass -p to override its port.

0.3 Create worktrees + clone node_modules

For each component, run (via Bash tool, sequential — these are quick):

git worktree add -B feat/blade-svelte/{Name} .claude/worktrees/{Name} origin/master

Then APFS-clone the dependency directories from the main checkout into the worktree so each worktree gets a fully private, mutable node_modules tree (see Assumptions below for the non-APFS fallback).

# Run from the main checkout root.
WT=.claude/worktrees/{Name}

# Idempotency guard: cp -cR onto an existing directory nests the clone inside it
# (e.g. $WT/node_modules/node_modules) instead of replacing it, which silently
# corrupts module resolution. Strip any prior copies before re-cloning.
# (Worktree is fresh out of `git worktree add` on the happy path — these only fire on retry.)
rm -rf "$WT/node_modules" \
       "$WT/packages/blade/node_modules" \
       "$WT/packages/blade-svelte/node_modules" \
       "$WT/packages/blade-core/node_modules"

# APFS only: `cp -c` returns non-zero on non-APFS filesystems, so we fall through
# to `yarn install` per worktree (~30–60s each). Stderr is logged, not swallowed.
if cp -cR "$(pwd)/node_modules" "$WT/node_modules" 2>>/tmp/blade-migration-clone.log; then
  [ -d packages/blade/node_modules ]        && cp -cR "$(pwd)/packages/blade/node_modules"        "$WT/packages/blade/node_modules"
  [ -d packages/blade-svelte/node_modules ] && cp -cR "$(pwd)/packages/blade-svelte/node_modules" "$WT/packages/blade-svelte/node_modules"
  [ -d packages/blade-core/node_modules ]   && cp -cR "$(pwd)/packages/blade-core/node_modules"   "$WT/packages/blade-core/node_modules"
else
  # Fallback: non-APFS filesystem (or other clone failure).
  echo "APFS clone failed; see /tmp/blade-migration-clone.log, falling back to yarn install"
  ( cd "$WT" && yarn install --prefer-offline --frozen-lockfile )
fi

If cloning fails for any other reason (corrupt node_modules, permissions, etc.), fall back to running yarn install inside the worktree.

Assumptions

Load-bearing preconditions for the clone-based approach. If any stop holding, revisit this step:

  • Yarn 1.x classic with nohoist: ["**"]and relative workspace symlinks. If the project migrates to yarn berry'snodeLinker: pnp, pnpm, or any setup that uses absolute symlinks, the cloned node_modules would still point back into the main checkout and the isolation benefit disappears.
  • macOS APFS filesystem. Linux/CI takes the yarn install fallback per worktree.

0.4 Start the shared React Storybook

From the first surviving slot's worktree ({Name1}), boot the React Storybook on 9009:

cd .claude/worktrees/{Name1}/packages/blade && yarn react:storybook

Run via Bash tool with run_in_background: true.

Poll curl -s http://localhost:9009 every 5s up to 60s. Once it returns HTML, continue.

On timeout, log to batch-status.md notes and continue (Verify skips Step 3, other gates still run).

0.5 Initialize batch-status.md

Write .claude/artifacts/batch-status.md in the main checkout with this layout:

# Batch Migration Status

React Storybook (shared): http://localhost:9009

| #   | Component | Tier | Worktree                  | Branch                     | Svelte Port | Status          | Iteration | PR  | Notes |
| --- | --------- | ---- | ------------------------- | -------------------------- | ----------- | --------------- | --------- | --- | ----- |
| 1   | Alert     | -    | .claude/worktrees/Alert   | feat/blade-svelte/Alert    | 6107        | ⏳ plan-pending | -         | -   | -     |
| 2   | Card      | -    | .claude/worktrees/Card    | feat/blade-svelte/Card     | 6207        | ⏳ plan-pending | -         | -   | -     |
| 3   | Checkbox  | -    | .claude/worktrees/Checkbox| feat/blade-svelte/Checkbox | 6307        | ⏳ plan-pending | -         | -   | -     |

Tier is filled in after Plan completes. PR is filled in after Step 6.

Update this file after every status change.


Step 1: Plan (parallel)

Spawn one Plan agent per component (parallel pattern — see pre-flight).

Per Agent call:

  • subagent_type: plan-svelte-migration
  • description: Plan {Name} migration
  • run_in_background: false
  • prompt: see template below

Prompt template:

Plan the migration of the {Name} React component to Svelte 5.

- Component name: {Name}
- Worktree (absolute base): {WorktreeAbs}

ALWAYS use absolute paths prefixed with the worktree. Write artifacts to:
- {WorktreeAbs}/.claude/artifacts/{Name}/discovery-report.md
- {WorktreeAbs}/.claude/artifacts/{Name}/migration-plan.md

Follow the steps in your agent definition.

{WorktreeAbs} = <main checkout absolute path>/.claude/worktrees/{Name}.

After all Plan agents return, verify both artifacts exist at:

  • {WorktreeAbs}/.claude/artifacts/{Name}/discovery-report.md
  • {WorktreeAbs}/.claude/artifacts/{Name}/migration-plan.md

Update batch-status.md per component:

  • On success: Status = ✅ planned, fill Tier from the discovery report.
  • On failure: Status = ❌ plan-failed, capture the failure reason in Notes.

Step 1.5: Self-heal misplaced artifacts

If either artifact is missing from {WorktreeAbs}/.claude/artifacts/{Name}/, check the main-checkout fallback at {MainCheckout}/.claude/artifacts/{Name}/. If found there, the agent resolved a relative path against its CWD instead of the worktree — move them in before marking failure:

mkdir -p {WorktreeAbs}/.claude/artifacts/{Name}
mv {MainCheckout}/.claude/artifacts/{Name}/{discovery-report,migration-plan}.md \
   {WorktreeAbs}/.claude/artifacts/{Name}/ 2>/dev/null

If still missing → ❌ plan-failed. Apply the same fallback to Execute (patch-request.md) and Verify (verification-report.md, screenshots/) outputs.

Step 2: Dependency Resolution

After all Plans complete, read every successful component's discovery-report.md and build the per-component dependency list.

Dependency workarounds (deps with workarounds do not require migration; skip them when resolving the graph):

Dependency Workaround
Box / BaseBox use <div> with classes (no migration needed)
Icons placeholder prop type (IconComponent = unknown)
blade-core (~utils/, ~tokens/) already framework-agnostic, import directly

For each component:

  1. Read its dependencies list from the discovery report.
  2. For every unmigrated dependency:
    • Matches the workarounds table → skip it.
    • Also in this batch → mark this component 🟡 deferred-to-next-batch (dep name in Notes), drop it from the active set, tell the user to re-run it after the dep's PR merges.
    • Not in this batch, no workaround → mark ❌ blocked-by-dep (dep name in Notes); tell the user to migrate the dep first.

Remaining components proceed independently.

Step 3: Plan Gate (per-component, sequential)

For each successfully-planned, non-deferred, non-blocked component:

  1. Skip the gate if the component's tier is simple. Auto-approve and continue.
  2. Otherwise, present the component's migration-plan.md to the user and ask for approval.
  3. On reject: re-run that component's Plan (Step 1) with the user feedback appended to the prompt. Max 3 reject cycles per component; after 3 → mark ❌ plan-rejected and continue with the others.
  4. On approve: mark Status = 🟢 plan-approved.

This gate is sequential because it requires human input. The components themselves run independently afterwards.

Step 4: Execute (parallel)

Spawn one Execute agent per plan-approved component (parallel pattern — see pre-flight).

Per Agent call:

  • subagent_type: execute-svelte-migration
  • description: Execute {Name} migration
  • run_in_background: false
  • prompt: see template below

Prompt template (Full mode):

Implement the {Name} Svelte component.

- Component name: {Name}
- Mode: Full
- Worktree (absolute base): {WorktreeAbs}

ALWAYS use absolute paths prefixed with the worktree.
- Migration plan: {WorktreeAbs}/.claude/artifacts/{Name}/migration-plan.md
- React source: {WorktreeAbs}/packages/blade/src/components/{Name}/

Follow the Full mode steps in your agent definition.

Update batch-status.md as each finishes:

  • Success → Status = 🛠️ executed.
  • Failure → Status = ❌ execute-failed, capture reason.

Step 5: Verify (parallel)

Spawn one Verify agent per executed component (parallel pattern — see pre-flight).

Per Agent call:

  • subagent_type: verify-svelte-migration
  • description: Verify {Name} migration
  • run_in_background: false
  • prompt: see template below

Prompt template:

Verify the {Name} Svelte component.

- Component name: {Name}
- Worktree (absolute base): {WorktreeAbs}
- React Storybook port: {ReactPort}
- Svelte Storybook port: {SveltePort}

ALWAYS use absolute paths prefixed with the worktree.
- Discovery report: {WorktreeAbs}/.claude/artifacts/{Name}/discovery-report.md
- Write report to: {WorktreeAbs}/.claude/artifacts/{Name}/verification-report.md

Boot Storybooks on the assigned ports if not already running. Do NOT kill them
when finished — the orchestrator needs them up for the final review gate.
Follow the steps in your agent definition.

{ReactPort} = 9009 (shared, started by Step 0.4). {SveltePort} is the slot's Svelte port from Step 0.2 (e.g., 6207).

Verify spawns Execute(patch) itself when it finds parity gaps. You only see the terminal status.

Update batch-status.md per component:

  • PASS / PASS-WITH-WARNINGS → Status = ✅ verified.
  • FAIL → Status = ❌ verify-failed.

Step 6: Final Gate + PR (per-component, parallel)

For each verified component:

6.1 Final review

Present verification-report.md + screenshots to the user. Storybook URLs for spot-check:

  • Svelte: http://localhost:{SveltePort}/iframe.html?id=components-{name}--{story}
  • React: http://localhost:9009/iframe.html?id=components-{name}--{story}

Both Storybooks remain running.

On reject: ask the user what to fix. Re-spawn Verify (or Execute → Verify) as appropriate. Max 2 reject cycles, then mark ❌ final-rejected and continue.

6.2 Commit, push, open PR

On approve, run inside the worktree:

# Generate changeset (bump types follow recent solo-component precedent: patch/patch).
cd .claude/worktrees/{Name}
mkdir -p .changeset
cat > .changeset/blade-svelte-{name}-component.md <<'EOF'
---
'@razorpay/blade-core': patch
'@razorpay/blade-svelte': patch
---

feat(blade-svelte): add {Name} component
EOF

# Stage migrated files. Exact paths come from the discovery report's "File Plan" section.
git add packages/blade-svelte/src/components/{Name} \
        packages/blade-core/src/styles/{Name} \
        packages/blade-svelte/src/components/index.ts \
        packages/blade-core/src/styles/index.ts \
        .changeset/blade-svelte-{name}-component.md

# (Add any other modified files surfaced by `git status` that belong to this component.)

git commit -m "feat(blade-svelte): {Name} component"
git push -u origin feat/blade-svelte/{Name}

gh pr create \
  --base master \
  --title "feat(blade-svelte): {Name} component" \
  --body "$(cat <<'EOF'
## Description

Migrates the React {Name} component to Svelte 5.

## Changes

- Adds `packages/blade-svelte/src/components/{Name}/`
- Adds `packages/blade-core/src/styles/{Name}/`
- Re-exports {Name} from `packages/blade-svelte/src/components/index.ts`
- Re-exports CSS from `packages/blade-core/src/styles/index.ts`

## Component Checklist

- [ ] Update Component Status Page
- [ ] Perform Manual Testing in Other Browsers
- [ ] Add KitchenSink Story
- [ ] Add Interaction Tests (if applicable)
- [x] Add changeset

EOF
)"

Capture the PR URL from gh pr create output and update batch-status.md PR column.

Step 7: Cleanup (manual, after all PRs merge)

Do not auto-remove worktrees. The user may want to keep them for hotfixes.

Print a summary at the end of the run:

Pipeline complete.
Worktrees still in place at .claude/worktrees/{Name}/.
To remove a worktree after its PR merges:

  git worktree remove .claude/worktrees/{Name}

Storybooks still running:
- React (shared) on port 9009, hosted by .claude/worktrees/{Name1}/packages/blade
- Svelte per worktree on its slot's port (6107, 6207, ...)

Remove the React-host worktree last — it kills the shared server for everyone.

Failure Handling Summary

Scenario Effect on this component Effect on others
Component already migrated Dropped at Step 0 Continue
React source not found Dropped at Step 0 Continue
Plan agent fails ❌ plan-failed Continue
Unmigrated dependency, no workaround ❌ blocked-by-dep Continue
Dependency cycle detected ❌ dep-cycle (all in cyc) Continue
User rejects plan 3 times ❌ plan-rejected Continue
Execute agent fails (incl. retries) ❌ execute-failed Continue
Verify agent FAIL (after 6 iterations) ❌ verify-failed Continue
User rejects final review 2 times ❌ final-rejected Continue

A failed component in any phase does not block the others. The orchestrator records the failure in batch-status.md and continues processing the remaining components.

Status Vocabulary

⏳ plan-pending✅ planned / ❌ plan-failed 🟢 plan-approved / 🟡 deferred-to-next-batch / ❌ blocked-by-dep / ❌ dep-cycle / ❌ plan-rejected 🛠️ executed / ❌ execute-failed ✅ verified / ❌ verify-failed 🚀 pr-opened / ❌ final-rejected

Install via CLI
npx skills add https://github.com/razorpay/blade --skill migrate-to-svelte
Repository Details
star Stars 624
call_split Forks 184
navigation Branch main
article Path SKILL.md
More from Creator