modernize-flp-sandbox
Use when migrating UI5 apps from legacy FLP sandbox to the new sandbox (New Sandbox). Triggers on /modernize-flp-sandbox, mentions of "flpSandbox.html", "sap-ushell-config", or requests to update sandbox configuration.
How do I install this agent skill?
npx skills add https://github.com/ui5/plugins-coding-agents --skill modernize-flp-sandboxIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The skill automates the migration of SAP UI5 applications from legacy FLP sandbox to the newer New Sandbox environment. It performs analysis of project files, transforms HTML and JavaScript files to match current standards, and manages application dependencies. The skill includes a rollback mechanism using Git or local backups and automates framework versioning via NPM metadata.
- Socketpass
No alerts
- Snykwarn
Risk: MEDIUM · 1 issue
What does this agent skill do?
New Sandbox Migration Skill
Migrate a UI5 application from the legacy FLP sandbox to New Sandbox. You analyze, transform, verify, and report. The skill is fully automatic for the core migration; for adjacent concerns (see scope below) it asks the consumer before acting.
Read references/operations.md now — it is the operational reference for
all HTML / JSON / hook-module transformations.
Read references/sandbox-config-surface.md now — it is the ground-truth
description of what New Sandbox allows, blocks, and silently overrides.
All §6 rules cite this file rather than restating constraints inline.
Scope — three layers
-
Core migration — actions necessary for the app to boot under New Sandbox. Performed unconditionally; fails migration if impossible. Triggers: §6a, §6b, §6c, §6f.
-
Test infrastructure — gets the test suite running again without changing test semantics. Safe rewrites applied unconditionally (resource-root rebind, OPA bootstrap extraction). Anything touching test logic: detect, report, offer on request. Triggers: §6d, §6e, §6g, §6h, §6j, §6l.
-
Adjacent migrations & advisories — concerns surfaced by the migration but not part of it. Detect and report only; no rewrites applied.
- QUnit 1.x → 2.x patterns (§6i) — advisory.
- Shell-feature tests (§6j, §6l) — advisory.
- Deprecated ushell services (§6k) — detected in core pass, reported as "Manual action required" (hard runtime block).
1. Detect App Root
If the user provided a path argument, use it. Otherwise, detect from CWD:
- Look for
package.json+webapp/in CWD and parent directories - If not found, tell the user: "Could not detect a UI5 app root. Please run from inside
the app directory or provide a path:
/modernize-flp-sandbox ~/path/to/app"
Set APP_ROOT to the resolved absolute path.
2. Ensure Clean State (Pre-Migration)
Before touching any files, secure a rollback point.
If git repository detected:
cd $APP_ROOT
git stash push -m "modernize-flp-sandbox: pre-migration backup $(date +%Y%m%d-%H%M%S)"
Save the stash ref for rollback. If nothing to stash, the tree is already clean.
If no git repository:
mkdir -p $APP_ROOT/webapp/test/.migration-backup
cp $APP_ROOT/webapp/test/flpSandbox*.html $APP_ROOT/webapp/test/.migration-backup/ 2>/dev/null
cp $APP_ROOT/webapp/test/fioriSandboxAppConfig.json $APP_ROOT/webapp/test/.migration-backup/ 2>/dev/null
3. Analyze
Read all HTML files under $APP_ROOT/webapp/test/ (recursive) and build a pattern inventory.
Detect legacy sandbox HTML files — a file is legacy if it contains ANY of:
id="sap-ushell-bootstrap"(sandbox bootstrap script tag)window["sap-ushell-config"]assignmentsrc=referencingsandbox2.js(intermediate New Sandbox old-style bootstrap — predatesSandboxBootTask.js; treat identically to other legacy patterns)
OPA test-page (does NOT receive SandboxBootTask / boot-manifest): A file matches legacy detection AND ALSO contains ALL of:
QUnit.configorQUnit.config.autostartassignment, ORwindow.QUnit = {sap.ui.require(with entries fromsap/ui/qunit/,sap/ui/test/, orsap/ui/thirdparty/qunit
These files are NOT migrated as sandbox HTMLs. They receive: (a) §6g bootstrap extraction into sibling JS, (b) §6i QUnit 1.x detection → sub-skill handoff on consent. The §6g HTML edits:
- Remove
<script src="../ushellConfig.js">and<script src="...sandbox.js" id="sap-ushell-bootstrap"> - Replace inline QUnit/OPA bootstrap with direct script-tag loading (qunit-2.css, qunit-2.js, qunit-junit.js) +
<script src="AllJourneys.js"> - Create
AllJourneys.jsfrom inline content (see §6g) - Update
data-sap-ui-preload="async"→data-sap-ui-async="true", renamedata-sap-ui-compatVersion→data-sap-ui-compat-version,data-sap-ui-resourceroots→data-sap-ui-resource-roots - Do NOT add
SandboxBootTask.js,boot-manifest, or<div id="canvas">
Externalized ushell config detection:
For each <script src="..."> in any HTML under webapp/test/, check if the referenced file contains an assignment to the ushell-config object via:
window["sap-ushell-config"] = …/globalThis[…]/self[…]Object.assign(<global>["sap-ushell-config"], …)<global>["sap-ushell-config"].<prop> = …(property-write after initializer)
Files that only read the config do NOT qualify. Record each qualifying file as externalUshellConfigFile. For migration: parse the config identically to inline, remove the <script> include from all referencing HTMLs, delete the file in Step 9.
For each legacy HTML file, record:
- Filename, has mock server?, has RTA/LREP?, has custom plugins?, has deprecated services?
- Has
locate-reuse-libs.js? (flag for Gap-Report) - Existing
data-sap-ui-libs,data-sap-ui-resourceroots/data-sap-ui-resource-roots,data-sap-ui-theme,data-sap-ui-languagevalues (carry over, rename to kebab-case)
Check for existing New Sandbox artifacts:
fioriSandboxAppConfig.json— exists? (will be created/overwritten)sandbox/directory — for each*.jsfile, classify:legacyif containsContainer.createRendererInternalORattachRendererCreatedEventOR does NOT containexecute:. Capture for in-place rewrite (§6c). Capture top-level namespace declarations aspreservedDeclarations[](regex:^\s*sap(\.[a-zA-Z0-9_]+)+\s*\?\?=\s*\{\}andglobalThis.sap/window.sapchains; fallback: copy verbatim until firstsap.ui.define(or IIFE).modernif exports{ execute: ... }. Reuse untouched.- Multiple legacy files: rewrite ALL. Only one wired via
beforeFlpStart— prefer name containingInit(notMock). Emit Gap-Report note.
- FakeLREP file (
*Lrep*.json/*LRep*.jsonunderwebapp/test/) — note exact filename forrtareference.
Check for OPA test wiring:
- OPA test runner HTML (default
webapp/test/integration/opaTests.qunit.html; fallback: any*qunit.htmlunderwebapp/test/integration/referencing the legacy mockserver HTML) — record value matching"../flpSandboxMockServer"→ triggers §6d. - OPA Common-helper file (default
webapp/test/integration/pages/Common.js; fallback: any*.jsunderwebapp/test/integration/containingiStartMyAppInAFrame(/getFrameUrl(ANDsap.ui.require.toUrl() — triggers §6e.
Scan for deprecated ushell service usage:
Search *.js under webapp/ (excluding test/, resources/, *.min.js) for getService(<name>) / getServiceAsync(<name>) where <name> is one of the 12 deprecated services in references/sandbox-config-surface.md §5. Record file, line, service name, bound variable. Surface in Gap-Report (§8); follow-up in §6k.
Check UI5 framework version:
Read ui5.yaml and ui5-local.yaml for framework.version. New Sandbox requires >= 1.147. If below, auto-bump:
- Find latest stable SAPUI5 version >= 1.147
- Write to
ui5.yamlin-place - Log: "Auto-bumped ui5.yaml framework version from
<old>to<new>(New Sandbox requires >= 1.147)." - Note in Gap-Report.
3a. Hook Dependency Graph Scan
Identifies eager sap/ushell/* imports in the transitive AMD graph reachable from the beforeFlpStart hook (Hard Constraint H2 — see references/sandbox-config-surface.md §1). Detail and rewrite recipe in references/operations.md §6f.
This step builds inventory only. Action happens in §6f (after hook is generated in §6c).
Record:
hookEntryModule— resolve from existingfioriSandboxAppConfig.jsonbeforeFlpStart, or detect from legacy hook in §3 (typicallywebapp/test/sandbox/fioriSandboxInit.js).hookGraphRoots[]— additional modules the hook requires directly (from inspecting legacy hook body).
4. Scenario Wizard (only when multiple flp*.html found)
Skip if only one legacy sandbox HTML found.
New Sandbox uses a single fioriSandboxAppConfig.json — multiple HTML files cannot have independent configurations. Solution: consolidate into one HTML with a URL parameter selecting the scenario at runtime.
Non-interactive mode: Check for $APP_ROOT/webapp/test/wizard-answers.json. If exists, use its values directly (CI/scripted migrations). Format:
{
"scenarios": [
{ "file": "flpSandbox.html", "id": "default", "description": "Live backend" },
{ "file": "flpSandboxMockServer.html", "id": "mockserver", "description": "Mock server" }
],
"defaultScenario": "mockserver",
"targetHtml": "flpSandbox.html",
"paramName": "scenario"
}
Ask the user:
- "What scenario does each file represent?" (e.g., "mockserver", "cdm", "default/no-mock")
- "Which scenario should be default?" (Recommend:
mockserver— works offline) - "Which HTML filename should be kept?"
- "URL parameter name?" (Default:
scenario; project-specific name avoids collisions)
Record: target HTML, scenario list, default scenario ID, parameter name, obsolete HTML files.
URL convention: flpSandbox.html?scenario=mockserver. No param → default scenario. Document chosen name in README.
5. Pre-Migration Checklist
Before writing files, verify: APP_ROOT set (§1), rollback secured (§2), inventory built (§3), wizard answers available if needed (§4). All good → proceed to §6.
6. Migrate
Execute all transformations. If any write fails, jump to ROLLBACK (§10).
Scope — files modified:
- All
flp*.htmldetected in §3 → full transformation (§6a–§6c) - OPA test-page HTMLs → bootstrap extraction (§6g)
- OPA test runner HTML → resource-root rebind (§6d)
- QUnit test suite index → journey-URL rewrite (§6g)
- OPA Common-helper → URL-param injection (§6e)
webapp/test/fioriSandboxAppConfig.json→ create/overwrite (§6b)webapp/test/sandbox/*.jslegacy hooks → rewrite in-place (§6c)- Files in
beforeFlpStartAMD graph → auto-applyreferences/native-replacements.md(§6f) - OPA test code under
webapp/test/integration/and app code → §6h–§6l mechanical rewrites
Out of scope: unitTests.qunit.html, unit/, *.qunit.js.
When flpSandboxMockServer.html is referenced by opaTests.qunit.html, migrate it (§6a) AND rebind the OPA reference (§6d) together.
Each subsection below states what/when/which-layer. Full algorithms live in references/operations.md.
6a. Transform legacy HTML file(s) in-place
Layer: core. Unconditional on every legacy sandbox HTML.
Removes legacy ushell-config write, sap-ushell-bootstrap script tag, obsolete bootstrap attributes. Adds SandboxBootTask.js (or CDN variant), boot-manifest attributes, <div id="canvas">. Merges resource roots across variants, always adds sandbox root.
→ references/operations.md §6a
6b. Create or update fioriSandboxAppConfig.json
Layer: core. Honors only keys in references/sandbox-config-surface.md §2.
Builds JSON from parsed legacy config. Splits application keys on first - into semanticObject + action. Strips query strings from rootPath, ensures trailing slash. Aggregates tiles across variants when Wizard ran. Adds beforeFlpStart, rta, plugins only when warranted.
→ references/operations.md §6b
6c. Create or rewrite hook module
Layer: core. Backs beforeFlpStart, runs before applyUshellConfig. Contract: no globalThis["sap-ushell-config"] touch, no eager sap/ushell/* require (see references/sandbox-config-surface.md §8).
Produces single-scenario hook (mock-server only) or multi-scenario hook (URL-parameter switch). Rewrites legacy hooks in place preserving namespace declarations. Falls back to minimal no-op when no mock server needed.
→ references/operations.md §6c
6d. Rebind OPA resource roots
Layer: test-infra. Unconditional (no semantics change).
In OPA test runner HTML, rewrites data-sap-ui-resource-roots pointing at legacy mockserver HTML to point at migrated target.
→ references/operations.md §6d
6e. Inject scenario URL parameter into Common.js
Layer: test-infra. Only when Wizard consolidated multiple HTMLs.
Injects scenario parameter into iframe URL before the # hash (so window.location.search sees it).
→ references/operations.md §6e
6f. Resolve hook-graph ushell dependencies
Layer: core. Hard runtime block H2 — no sap/ushell/* may be eagerly required from hook's transitive AMD graph.
Walks AMD closure from hookEntryModule (§3a). Classifies each sap/ushell/* hit as trivial-replaceable / architectural-relocatable / manual-required. Auto-applies recipes from references/native-replacements.md or emits Gap-Report entry.
→ references/operations.md §6f
6g. Extract OPA bootstrap into a sibling JS file
Layer: test-infra. Mechanical extraction.
Extracts inline <script> block into sibling .js (default AllJourneys.js). Converts QUnit 1.x globals and jQuery.sap.getUriParameters to modern equivalents. Updates test-suite index to URLSearchParams-style journey URLs.
→ references/operations.md §6g
6h. Detect bindings to legacy ushell-config values
Layer: test-infra (auto-fix where mechanical, report otherwise).
Identifies code hard-coding values that were free-form in legacy config but are now derived differently under New Sandbox. Categories: A tile-derived (auto-fix), B user-profile (auto-fix), C personalization (auto-fix), D fixed-by-default (report only). See references/sandbox-config-surface.md §3+§4.
→ references/operations.md §6h
6i. QUnit 1.x → 2.x patterns (handoff)
Layer: advisory. Actual rewrite in modernize-flp-sandbox-qunit sub-skill.
Detects, reports, offers to invoke sub-skill. Why surfaced: §6a adds data-sap-ui-async="true" — QUnit 1.x globals (module, ok, equal) throw ReferenceError in that mode. Root cause is independent of sandbox migration; the migration just exposes it.
→ references/operations.md §6i
6j. FLP shell-control awareness (reporting)
Layer: test-infra (report only). Detects OPA tests targeting FLP shell controls directly. Shell renders through Web Components in newer UI5 — tests matching sap.m.* shell controls break silently.
Patterns: S1 = sap.m control + shell-only id, S2 = type:"Back" matcher, S3 = non-WebComponent shell header calls.
→ references/operations.md §6j
6k. Deprecated ushell-services in app code (detection)
Layer: core detection. Every deprecated service is a hard runtime block at getService()/getServiceAsync() time (see references/sandbox-config-surface.md §5). App won't boot.
Scans app code, records hits, surfaces in Gap-Report as "Manual action required". No rewrite attempted.
→ references/operations.md §6k
6l. OPA tests exercising FLP-shell features (detection + ask)
Layer: test-infra (detection + ask). Detects journey tests dependent on FLP shell. Only offers to apply fix if recognised iframe-toggle pattern exists. Never modifies without consent.
Signals: shell-control selectors (§6j), deprecated service calls (§6k), convention-named page-object methods.
Probes: P1 = startMyAppInAFrame flag, P2 = iStartMyAppInAFrame helper, P3 = bespoke iframe URL builder.
→ references/operations.md §6l
8. Report
Always emit the Gap-Report:
== New Sandbox Migration Report ==
App: <app name from package.json>
Path: <APP_ROOT>
Status: ✓ SUCCESSFUL | ✗ FAILED
── Automatically applied ──────────────────────────────────────────────
✓/✗ webapp/test/<filename>.html (HTML transformed)
✓/✗ webapp/test/fioriSandboxAppConfig.json (created)
✓/— webapp/test/sandbox/<hookModule>.js (created / reused / rewrote-legacy)
✓/— rta: "<fakeLRep filename>" (written to config)
✓/— opaTests.qunit.html (resource root rebound)
✓/— Common.js (scenario URL param injected)
✓/— Hook-graph: <N> trivial replacement(s) auto-applied
── Manual action required ─────────────────────────────────────────────
✗ <ServiceName> (deprecated — hard runtime error): <file>:<line>
→ <successor from sandbox-config-surface.md §5>
✗ Custom FakeLrep connector at <path>
→ May have sap/ushell dependencies incompatible with beforeFlpStart.
✗ Hook-graph — architectural-relocatable: <module> uses <sap/ushell/X>
→ [diff sketch]
✗ Hook-graph — manual-required: <module> uses <sap/ushell/X> at load time
→ [textual sketch]
✗ <Layer-2 step> — pattern not recognised
→ [description]
── Advisory (no migration blocker) ───────────────────────────────────
ℹ <N> QUnit 1.x pattern(s) in <M> file(s)
ℹ Shell-control test patterns: <N> in <M> files (S1: ?, S2: ?, S3: ?)
ℹ locate-reuse-libs.js: script tag removed — deprecated pattern, safe.
ℹ <other non-standard patterns>
[On failure: "Rolled back. Partial attempt saved to webapp/test/.migration-attempt/"]
Sections with no findings are omitted from the report.
9. Legacy File Cleanup (REQUIRED — do not skip)
- No Wizard: legacy HTMLs transformed in-place — no extra files to delete.
- Wizard used: delete all obsolete HTML files (everything except target).
- Always: delete each
externalUshellConfigFile[].
rm <obsolete-file-1> <obsolete-file-2> ...
Verify: ls webapp/test/flp*.html — only the migrated target remains.
10. Rollback
On any failure during §6:
Save partial attempt:
mkdir -p $APP_ROOT/webapp/test/.migration-attempt
cp $APP_ROOT/webapp/test/*.html $APP_ROOT/webapp/test/.migration-attempt/ 2>/dev/null
cp $APP_ROOT/webapp/test/fioriSandboxAppConfig.json $APP_ROOT/webapp/test/.migration-attempt/ 2>/dev/null
Restore:
- Git:
git checkout -- webapp/test/ && git stash pop - No git:
cp $APP_ROOT/webapp/test/.migration-backup/* $APP_ROOT/webapp/test/
Emit Gap-Report with status FAILED.
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/ui5/plugins-coding-agents/modernize-flp-sandbox">View modernize-flp-sandbox on skillZs</a>