- name
- neo-to-cf-migration-orchestrator
- description
- Invoke this skill to orchestrate complete Neo to Cloud Foundry migration. Analyzes the Neo app, creates a migration plan, and dispatches each migration scenario (Jakarta, SDK, auth, persistence, destinations, etc.) to a separate subagent so the orchestrator's context stays lean across the full 5–14 step pipeline. Use when user says 'migrate Neo app', 'convert to CF', or 'Neo to Cloud Foundry migration'.
- disable-model-invocation
- false
- allowed-tools
- Agent, Read, Edit, Write, Grep, Glob, Bash(curl *), Bash(python3 *), Bash(mvn *), Bash(btp *), Bash(cf *), Bash(echo *), Bash(cat *), Bash(ls *), Bash(mkdir *), Bash(find *), Bash(grep *)
# Neo to Cloud Foundry Migration Orchestrator
Orchestrates the complete migration of SAP BTP Neo Java applications to Cloud Foundry.
## Purpose
This skill coordinates the end-to-end migration process by:
1. Analyzing the Neo application to detect required transformations
2. Creating a migration plan with skills in the correct order
3. **Dispatching each skill to its own subagent** so the orchestrator never loads the bodies of the 5–14 child skills into its own context
4. Validating the migration at each step using cheap filesystem and `mvn` checks
5. Generating the final deployment descriptor
## Orchestration Algorithm
This skill follows the **Orchestrator-Worker pattern** with a verification gate between steps:
- **Orchestrator** (this skill, running in your main context): plans, dispatches, verifies, and recovers. Holds only the plan, status log, and short worker reports.
- **Workers** (subagents you spawn via the `Agent` tool): each handles one self-contained, idempotent unit of work in a fresh context and returns a ≤30-line structured report.
- **Gate**: after each worker returns SUCCESS, the orchestrator advances to the next step. The worker's verification output is trusted — the orchestrator does NOT re-run the same verification.
### Dispatch tool
Every subagent in this skill is spawned via the `Agent` tool. Use `subagent_type: "general-purpose"` for migration steps and `subagent_type: "Explore"` for read-only detection fan-outs.
```
Agent(
subagent_type: "general-purpose", // or "Explore" for read-only
description: "<3-5 word summary>",
prompt: <the full prompt below>
)
```
Never emulate a subagent inline — that defeats the entire context-saving purpose.
### Concurrency policy
| Phase | Mode | Cap |
|-------|------|-----|
| Phase 1 detection | **Fan-out (parallel)** | Up to 13 Explore agents in one batch (one per skill detection). All read-only — no file conflicts. |
| Phase 3 execution | **Sequential** | 1 at a time. Feature skills mutate overlapping files (`pom.xml`, `web.xml`, `mtad.yaml` precursors); parallel runs would race. Step 3.3 (`mta-descriptor`) is run **inline** by the orchestrator — see Step 3.3 for why this is the one exception to the per-skill subagent rule. |
| Phase 4 verification | Sequential | 1 subagent. |
When fanning out in Phase 1, issue all `Agent` calls in a single message so they run concurrently (barrier-sync pattern).
### Failure & retry policy
- **Max retries per step: 2.** After 2 failed subagent attempts on the same step, stop and surface to the user — do not loop indefinitely.
- **Recoverable failures** (single-line tweak): fix in `$COPY_DIR` directly with `Edit`/`Write`, then re-run verification inline. Don't re-spawn a subagent for a one-line fix.
- **Structural failures** (skill needs to re-run): re-spawn with the original prompt plus `Previous attempt failed because: <reason>. Address it and retry.`
- **Hard stop conditions**: BOM version cannot be resolved from the registry; `mvn clean package` fails on a step that previously passed; `mtad.yaml` not produced in Phase 3.3 after 2 attempts.
### orchestrator.log format
Every dispatch outcome appends exactly one line to `.migration/orchestrator.log` in this canonical format:
```
<ISO-8601-timestamp> phase=<n> skill=<name> status=<SUCCESS|FAILED|PARTIAL> attempt=<k>
```
Append-only. Never edit prior lines. Both the Resume protocol below and Step 3.4's hygiene rules read and write this single format — do not introduce variants.
### Resume protocol
The orchestrator's own context may be summarized mid-migration. Resume is driven by `.migration/orchestrator.log` and `.migration/cf-migration-config.json`:
```
1. cat .migration/orchestrator.log
2. last_success = last line whose status=SUCCESS
resume_from = step immediately after last_success in the plan
3. PARTIAL is treated as FAILED for resume purposes:
if the last line has status=PARTIAL or FAILED, the resume_from is THAT step
(re-dispatch it; child skills are idempotent so re-running is safe).
4. If no log exists → start from Phase 0.
If log exists but plan absent → re-run Phase 1 + 2 (cheap, idempotent)
to rebuild the plan, then jump to resume_from.
5. Honor the max-retry cap: count the existing FAILED+PARTIAL lines for the same
skill in the log; if already at 2, stop and surface to user instead of re-dispatching.
```
Each child skill is required to be idempotent (re-running on already-migrated files is a no-op or detects the migrated state). This means the worst case of a duplicate dispatch is a wasted subagent call, never a corrupted workspace.
### Why subagents (motivation, kept for context)
Running each migration step inline would pull every child skill's `SKILL.md` (often hundreds of lines plus reference files) into the orchestrator's context. Across a full migration that's tens of thousands of tokens of skill bodies the orchestrator never needs to read — it only needs to know which skill to run next and whether the previous one succeeded.
| Phase | Inline or subagent? | Why |
|-------|---------------------|-----|
| Phase 0 — copy | Inline | Just `cp -r`, trivial. |
| Phase 1 — analysis | **Fan-out: 13 parallel Explore subagents** | Each does one skill's detection in parallel; orchestrator aggregates the small structured reports inline. |
| Phase 2 — planning | Inline | The orchestrator already has the aggregated detection results; it just renders the plan and asks the user. No new file reads. |
| Phase 3 — execution (feature skills, Steps 3.1–3.2) | **One general-purpose subagent per skill, sequential** | The original motivation — see the dispatch pattern below. |
| Phase 3 — execution (`mta-descriptor`, Step 3.3) | **Inline** in the orchestrator's context | One-shot, last step, and `mtad.yaml` is THE deliverable. The orchestrator already has the cross-skill rules and feature-skill list it needs to feed the descriptor; the subagent dispatch was a translation step that introduced drift. See Step 3.3. |
| Phase 4 — verification | **One general-purpose subagent** | `mvn clean package` output, recursive grep, file listings. The subagent returns pass/fail + last 10 lines on failure. |
See the per-phase dispatch prompts in **Phase 1.0**, **Phase 3** (Step 3.1–3.2 for subagent feature skills; Step 3.3 runs inline), and **Phase 4.0**.
## Artifact versions — resolve, don't invent
Never write an artifact version you remember from training data. SAP BOMs (`cf-tomcat-bom`, `sdk-modules-bom`, `cf-tomee-bom`, …) release frequently, and a number that doesn't exist in the registry breaks the BOM import with `Non-resolvable import POM`, which cascades into `'dependencies.dependency.version' is missing` for every dependency the BOM manages.
Resolve each SAP BOM with a one-line lookup against Maven Central — use this exact form, do not hand-pick a number:
```bash
latest_version () {
curl -fsS --max-time 10 \
"https://search.maven.org/solrsearch/select?q=g:$1+AND+a:$2&core=gav&rows=20&wt=json" \
| jq -r '.response.docs | map(select(.v | test("^[0-9]+(\\.[0-9]+)*$")))
| sort_by(-.timestamp) | .[0].v'
}
latest_version com.sap.cloud.sjb.cf cf-tomcat-bom
latest_version com.sap.cloud.sdk sdk-modules-bom
latest_version com.sap.cloud.sjb.cf cf-tomee-bom # only if migrating to TomEE
```
Each invocation prints exactly one line: the latest *release* version. The subagent must capture that line and substitute it for the `RESOLVED_*` placeholders the child skills (`sdk-replacement`, `keystore-credstore`, `tomee-runtime`) ship in their pom snippets. If a lookup returns empty or `curl` exits non-zero — network failure, registry change — the subagent must stop and report. Do **not** ask another subagent to "just pick a recent one," do **not** fall back to a number from training data, and do **not** patch a version into the descriptor yourself.
Other identifiers the skills prescribe — buildpack names (`sap_java_buildpack_jakarta`, not `sap_java_buildpack`), service names, plan names — are fixed strings, not version numbers. Use them exactly as the child skill specifies.
## Full Subaccount Migration Order
When migrating a complete Neo subaccount (platform configuration + one or more applications), execute the phases in this order. Phases 1 and 3 run **once per subaccount**; Phase 2 runs **once per application**.
```
PHASE 0: Tooling setup — once
Install CF CLI + BTP CLI, login to both
PHASE 1: Subaccount export — once (read-only, Neo side)
subaccount-trust-migrator ← export + import in one pass (in-memory, no disk files)
subaccount-roles-export ← export now; import deferred to Phase 5
(destinations are NOT exported to file — migrated directly in Phase 3 via neo-destinations-keystores-migrator)
PHASE 2: Per-app code migration — repeat for each app directory
For each app:
jakarta-java25-migration
sdk-replacement
authentication-xsuaa ← creates xs-security.json + role-collections
[feature skills: destinations, persistence-hana, etc.]
mta-descriptor
PHASE 3: Platform import — once (CF side, before deploy)
(trust already imported in Phase 1 by subaccount-trust-migrator)
(roles-import is NOT here — deferred)
PHASE 4: Deploy all apps — once per app
mvn clean package -DskipTests
cf deploy . -f
PHASE 5: Post-deploy — once (after ALL apps deployed)
neo-destinations-keystores-migrator ← requires CF apps to exist for app-level binding
subaccount-roles-import ← NOW: live XSUAA appIds exist
assigns role-templates + users to collections
```
> **Why roles-import is last:** `btp add security/role` requires the live XSUAA `appId` (e.g. `myapp!t1234`) which is only assigned after the first `cf deploy`. Role collections are created by `authentication-xsuaa` via `xs-security.json` + deployment — `subaccount-roles-import` only links role-templates into those collections and assigns users.
### Multi-App Notes
For a subaccount with multiple applications:
- Phases 0, 1, 3, and 5 run **once** for the whole subaccount
- Phase 2 runs **once per app directory** — each app gets its own `xs-security.json`, `approuter/`, and `mtad.yaml`
- Phase 4 runs **once per app** — deploy each app separately with `cf deploy . -f` from its directory
- Phase 5 reads all apps from `.migration/neo-roles.json` and resolves each against live CF XSUAA apps in a single pass — run it only after **all** apps from Phase 4 are successfully deployed
- If one app fails to deploy, `subaccount-roles-import` will flag it and can be re-run after the issue is fixed
---
## Trigger
This skill is triggered when the user requests:
- "Migrate my Neo app to CF"
- "Convert this application to Cloud Foundry"
- "Migration from Neo to Cloud Foundry"
- "Help me migrate to CF"
- Any request involving Neo to CF migration
## Migration Workflow
```
+------------------------------------------------------------------+
| MIGRATION WORKFLOW |
+------------------------------------------------------------------+
| |
| PHASE 1: ANALYSIS |
| ----------------- |
| Scan the application to identify which skills are needed: |
| - pom.xml - Neo dependencies, Java version |
| - web.xml - Resource references, auth config |
| - Java files - Neo API imports |
| |
| PHASE 2: PLANNING |
| ---------------- |
| Create ordered migration plan based on detection results |
| Present plan to user for approval |
| |
| PHASE 3: EXECUTION |
| ----------------- |
| Apply skills in dependency order: |
| 1. Foundation skills (always required) |
| 2. Feature skills (based on detection) |
| 3. Deployment skill (always last) |
| |
| PHASE 4: VERIFICATION |
| --------------------- |
| - Compile the application |
| - Verify no Neo imports remain |
| - Validate mtad.yaml structure |
| |
+------------------------------------------------------------------+
```
## Skill Dependency Order
Skills MUST be applied in this order:
```
FOUNDATION (Always Required)
|
+-> 1. jakarta-java25-migration
| Migrate to Java 25 and Jakarta EE 10
|
+-> 2. sdk-replacement
Replace Neo Java Web API with SAP Cloud SDK
|
+-> 3. dependency-compatibility (if third-party libs detected)
Resolve library compatibility for Java 25 / Jakarta / HANA Cloud
FEATURES (Based on Detection)
|
+-> 4. approuter-setup (if web-facing app detected)
| Set up SAP Application Router
|
+-> 5. authentication-xsuaa (if auth detected)
| Set up XSUAA security configuration
|
+-> 6. persistence-hana (if DataSource detected)
| Configure HANA Cloud database
|
+-> 7. destinations (if ConnectivityConfiguration detected)
| Configure Destination service
|
+-> 8. connectivity-onpremise (if on-premise proxy detected)
| Enable Cloud Connector connectivity
|
+-> 9. mail-destinations (if mail session detected)
| Configure mail via destinations
|
+-> 10. document-management-sdm (if EcmService detected)
| Migrate to Document Management Service
|
+-> 11. keystore-credstore (if KeyStoreService detected)
| Migrate to Credential Store
|
+-> 12. tomee-runtime (if EJB detected - ALTERNATIVE to Tomcat)
| Configure TomEE container
|
+-> 13. monitoring-logging (optional)
Set up Cloud Logging
DEPLOYMENT (Always Last)
|
+-> 14. mta-descriptor
Generate mtad.yaml deployment descriptor
```
## Phase 0: Create Migration Copy
Before analyzing or modifying anything, create a sibling copy of the application directory. All migration work — by this orchestrator and every skill it invokes — is done on the copy.
```bash
APP_DIR=$(pwd)
APP_NAME=$(basename "$APP_DIR")
COPY_DIR="$(dirname "$APP_DIR")/${APP_NAME}-cf-migration"
if [ -d "$COPY_DIR" ]; then
echo "Migration copy already exists at $COPY_DIR — using it."
else
cp -r "$APP_DIR" "$COPY_DIR"
echo "Created migration copy at $COPY_DIR"
fi
cd "$COPY_DIR"
```
Now that we are inside the copy, create `.migration/` and save the config there:
```bash
mkdir -p .migration
```
Save the paths to `.migration/cf-migration-config.json` (create or update):
```json
{
"sourceAppDir": "<original APP_DIR>",
"migrationAppDir": "<COPY_DIR>"
}
```
> All subsequent steps and all invoked skills must operate inside `$COPY_DIR`. The original `$APP_DIR` is never modified. The `.migration/` directory is inside the copy, not the original.
## Phase 1: Analysis
### Step 1.0: Fan Out Detection to Parallel Explore Subagents
Phase 1 runs ~13 detection sweeps over every `pom.xml`, `web.xml`, and Java source tree in the project. The sweeps are **independent and read-only** — perfect for fan-out. Each sweep goes to its own `Explore` subagent, all dispatched in a single message so they run concurrently (barrier-sync pattern). The orchestrator then aggregates the 13 short reports inline (cheap, decision-shaped).
#### Step 1.0a: First, discover the project layout once (inline)
View on GitHub