npx skills add ...
npx skills add arc-mcp/arc-1 --skill migrate-custom-code
ATC-driven S/4HANA custom code migration — runs ATC readiness checks, groups findings, and generates clean-core replacement code following modern ABAP patterns. Use when asked to "migrate this Z program", "fix ATC findings", "modernize this code for S/4HANA", "clean-core fixes", or "ABAP Cloud readiness for this object".
npx skills add arc-mcp/arc-1 --skill migrate-custom-code
ATC-driven S/4HANA custom code migration assistant with automated fix proposals.
This skill replicates SAP Joule's "Custom Code Migration" capability by combining ARC-1 (SAP system access) with mcp-sap-docs (documentation & best practices). It runs ATC readiness checks, groups and explains findings, and generates replacement code following modern ABAP patterns.
| Setting | Default | Rationale |
|---|---|---|
| Object type | Auto-detect via SAPSearch | Don't make user look up the type |
| ATC variant | System default | Omitting variant makes ARC-1 resolve and send systemCheckVariant |
| Scope | fix (explain + fix proposals) | Most actionable output |
| Priority filter | All priorities (start with Priority 1 errors) | Don't miss anything, but fix most critical first |
The user provides an object or package to check (e.g., ZCL_SALES_HANDLER, Z_REPORT_POSTING, ZSALES_PKG).
Only the object name is required. If the user provides just an object name, auto-detect the type and proceed with the default variant and fix scope.
Optionally, the user may specify:
S4HANA_2023, S4HANA_READINESS; default: system default)explain (findings only) or fix (explain + fix proposals; default: fix)ATC gotchas (verified live on S/4HANA 2023):
- ATC skips
$TMP/ local objects. A check run against a$TMPobject resolves to an empty object set and returns zero findings — this is not "clean code", it's "not checked". Run ATC against objects in a transportable package. If the user points you at$TMPcode, say so and ask them to assign it to a transportable package first.- The check variant must exist on the system.
ABAP_CLOUD_READINESSis the standard name on SAP BTP ABAP / S/4HANA Cloud, but on-premise systems often shipS4HANA_READINESS_<release>(e.g.S4HANA_READINESS_2023) orSAP_CLOUD_PLATFORM_DEFAULTinstead. If the named variant is absent ARC-1 rejects the call (SAP would silently runDEFAULT) — list the real names withSAPDiagnose(action="atc_variants"), or omitvariantto bind the system's configured default.
If no variant was specified, run with the system default first (omitting variant makes ARC-1 resolve systemCheckVariant from ATC customizing and send it explicitly — SAP's own empty-checkVariant behavior would run the Code Inspector variant DEFAULT instead):
Then suggest a readiness variant if available (e.g., S4HANA_READINESS_2023, ABAP_CLOUD_READINESS). If a run returns zero findings on code you expect to be flagged, first confirm the object is not in $TMP (see the gotcha above).
This returns all objects in the package. Then run ATC on each object individually. For large packages, prioritize PROG, CLAS, FUGR, FUNC types first as they typically contain the most migration-relevant findings.
Organize findings into a structured summary. Deduplicate findings with the same checkTitle across multiple locations.
Present findings grouped by priority and category:
Group findings into these categories:
Ask the user: "Which findings should I explain and fix? (all / specific numbers / errors only)"
For each selected finding, provide a detailed explanation.
Show the code context around each finding (the line with the issue and a few lines before/after).
For specific simplification items:
For each finding, present:
For each fixable finding, generate replacement code based on documentation and modern ABAP patterns.
For each finding location, first check whether SAP provides a native quickfix:
If proposals are returned:
apply_quickfix returns ranged text deltas — it does not persist. Apply the returned deltas to the source yourself, then write the result via SAPWrite(action="update"). When applying multiple fixes to the same object, re-resolve each finding's line after each write (earlier edits shift later line numbers), or fix top-to-bottom.Quickfix availability is system-dependent. SAP only offers quickfix proposals where the system has the corresponding check/cloudification content installed. On a bare ABAP trial or a system without the Clean-Core remediation content,
getFixProposalsreturns[]and ATC findings carryhasQuickfix=false— that's expected, not a bug. In that case skip straight to LLM-generated fixes (the options below). Prefer SAP quickfixes only when they actually come back.
Match the proposal to the finding — don't blindly apply
proposals[0].quickfixreturns every proposal applicable at that source position, which often includes generic refactorings unrelated to the finding (verified live on S/4HANA 2023: a position returned "Rename", "Convert to attribute", "Convert to importing parameter", "Extract local variable", …). Pick the proposal whosename/descriptionactually addresses the finding; ignore the rest.
Refactoring-style proposals can't be auto-applied here. Proposals whose
uricontains/providers/refactoring/(rename / convert-to-X / extract / introduce) are interactive refactorings that ride the separate/sap/bc/adt/refactoringsmulti-step flow —apply_quickfixreturns HTTP 500 for them (verified live). They are not finding fixes; skip them for automated apply and fall back to an LLM-generated fix. Only proposals from non-refactoring providers apply cleanly throughapply_quickfix.
Present 4 options per finding:
Group related fixes that can be applied together (e.g., multiple deprecated statements in the same method).
Use these replacement patterns when generating fixes:
| Old Pattern | New Pattern | Notes |
|---|---|---|
CALL FUNCTION '<fm>' | Class method call | Find replacement class in documentation |
SELECT...ENDSELECT | SELECT INTO TABLE + loop | Avoid row-by-row processing |
MOVE-CORRESPONDING src TO dst | dst = CORRESPONDING #( src ) | New ABAP syntax |
READ TABLE itab WITH KEY k = v | line_exists( itab[ k = v ] ) or VALUE #( itab[ k = v ] OPTIONAL ) | Use table expressions |
FORM/PERFORM | Method call | Extract to class method |
DESCRIBE TABLE itab LINES lv_cnt | lv_cnt = lines( itab ) | Built-in function |
CALL METHOD obj->method | obj->method( ) | Functional call syntax |
CREATE OBJECT obj TYPE cls | obj = NEW cls( ) | NEW operator |
TRANSLATE str TO UPPER CASE | str = to_upper( str ) | Built-in function |
| Classic DB view | CDS view entity | Redesign required |
WRITE: / ALV list | Fiori / RAP service | Major redesign |
For method-level fixes in classes:
For full-source updates (programs, functions):
If syntax errors are introduced: revert by writing back the original source and report the issue.
Run ATC again to confirm findings are resolved:
Present a final summary:
| Error | Cause | Fix |
|---|---|---|
| ATC variant not found | Variant doesn't exist on this system (hard error — SAP would otherwise silently run DEFAULT) | List the real names with SAPDiagnose(action="atc_variants"), or omit variant for the system default |
| Object locked by another user | Object is being edited elsewhere | Inform user, suggest trying later or contacting the lock holder |
| Fix causes new syntax error | Generated replacement code is incorrect | Revert to original source, show the diff, suggest manual correction |
| No mcp-sap-docs available | Documentation MCP not configured | Explain findings using ATC finding text only, recommend user check SAP Help Portal |
| Package has too many objects | Large package with 100+ objects | Suggest breaking into smaller batches by sub-package or object type |
| ATC returns no findings | Object is already S/4HANA ready | Inform user — no findings is good news, suggest checking with a stricter variant |
| Transport required but not provided | Object is in a transportable package | Ask user for transport request number before applying fixes |
SAPRead(type="<type>", name="<object_name>")search("<checkTitle> S/4HANA migration")
search("<deprecated_api> replacement ABAP")search("<simplification_item_id> simplification list")sap_notes_search(q="<checkTitle>")
sap_notes_search(q="<deprecated_api> replacement")SAPDiagnose(action="quickfix", type="<type>", name="<object_name>", source="<current_source>", line=<finding_line>, column=0)SAPDiagnose(action="apply_quickfix", type="<type>", name="<object_name>", source="<current_source>", line=<finding_line>, column=0, proposalUri="<proposal_uri>", proposalUserContent="<proposal_user_content>")SAPWrite(action="edit_method", type="CLAS", name="<class>", method="<method>", source="<fixed_source>", transport="<transport>")SAPWrite(action="update", type="<type>", name="<object_name>", source="<fixed_source>", transport="<transport>")SAPRead(type="SYNTAX", objectType="<type>", name="<object_name>")SAPActivate(type="<type>", name="<object_name>")Migration Fix Summary for <object_name>
========================================
Findings fixed: <n>
Findings remaining: <n>
Findings needing manual fix: <n>
Fixed:
- [FIXED] Use of obsolete statement MOVE (3 occurrences)
- [FIXED] SELECT...ENDSELECT replaced with SELECT INTO TABLE
Remaining:
- [MANUAL] CL_GUI_ALV_GRID replacement requires UI redesign
- [SKIPPED] User skipped FORM/PERFORM migration
Next steps:
- Address remaining manual findings
- Run full regression tests
- Consider running ATC on dependent objects