次のような場合に使用: 従来のFLP サンドボックス(本番環境に近い試験環境)から新しいサンドボックスへとUI5アプリケーションを移行する場合。/modernize-flp-sandbox、「flpSandbox.html」や「sap-ushell-config」への言及、またはサンドボックス設定の更新リクエストで起動します。
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.
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.
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.
If the user provided a path argument, use it. Otherwise, detect from CWD:
package.json + webapp/ in CWD and parent directories/modernize-flp-sandbox ~/path/to/app"Set APP_ROOT to the resolved absolute path.
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
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= referencing sandbox2.js (intermediate New Sandbox old-style bootstrap — predates SandboxBootTask.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.config or QUnit.config.autostart assignment, OR window.QUnit = {sap.ui.require( with entries from sap/ui/qunit/, sap/ui/test/, or sap/ui/thirdparty/qunitThese 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:
<script src="../ushellConfig.js"> and <script src="...sandbox.js" id="sap-ushell-bootstrap"><script src="AllJourneys.js">AllJourneys.js from inline content (see §6g)data-sap-ui-preload="async" → data-sap-ui-async="true", rename data-sap-ui-compatVersion → data-sap-ui-compat-version, data-sap-ui-resourceroots → data-sap-ui-resource-rootsSandboxBootTask.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:
locate-reuse-libs.js? (flag for Gap-Report)data-sap-ui-libs, data-sap-ui-resourceroots/data-sap-ui-resource-roots, data-sap-ui-theme, data-sap-ui-language values (carry over, rename to kebab-case)Check for existing New Sandbox artifacts:
fioriSandboxAppConfig.json — exists? (will be created/overwritten)sandbox/ directory — for each *.js file, classify:
legacy if contains Container.createRendererInternal OR attachRendererCreatedEvent OR does NOT contain execute:. Capture for in-place rewrite (§6c). Capture top-level namespace declarations as preservedDeclarations[] (regex: ^\s*sap(\.[a-zA-Z0-9_]+)+\s*\?\?=\s*\{\} and globalThis.sap/window.sap chains; fallback: copy verbatim until first sap.ui.define( or IIFE).modern if exports { execute: ... }. Reuse untouched.beforeFlpStart — prefer name containing Init (not Mock). Emit Gap-Report note.*Lrep*.json / *LRep*.json under webapp/test/) — note exact filename for rta reference.Check for OPA test wiring:
webapp/test/integration/opaTests.qunit.html; fallback: any *qunit.html under webapp/test/integration/ referencing the legacy mockserver HTML) — record value matching "../flpSandboxMockServer" → triggers §6d.webapp/test/integration/pages/Common.js; fallback: any *.js under webapp/test/integration/ containing iStartMyAppInAFrame(/getFrameUrl( AND sap.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:
ui5.yaml in-place<old> to <new> (New Sandbox requires >= 1.147)."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 existing fioriSandboxAppConfig.json beforeFlpStart, or detect from legacy hook in §3 (typically webapp/test/sandbox/fioriSandboxInit.js).hookGraphRoots[] — additional modules the hook requires directly (from inspecting legacy hook body).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:
mockserver — works offline)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.
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.
Execute all transformations. If any write fails, jump to ROLLBACK (§10).
Scope — files modified:
flp*.html detected in §3 → full transformation (§6a–§6c)webapp/test/fioriSandboxAppConfig.json → create/overwrite (§6b)webapp/test/sandbox/*.js legacy hooks → rewrite in-place (§6c)beforeFlpStart AMD graph → auto-apply references/native-replacements.md (§6f)webapp/test/integration/ and app code → §6h–§6l mechanical rewritesOut 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.
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
fioriSandboxAppConfig.jsonLayer: 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
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
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
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
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
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
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
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
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
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
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
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.
externalUshellConfigFile[].rm <obsolete-file-1> <obsolete-file-2> ...
Verify: ls webapp/test/flp*.html — only the migrated target remains.
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 checkout -- webapp/test/ && git stash popcp $APP_ROOT/webapp/test/.migration-backup/* $APP_ROOT/webapp/test/Emit Gap-Report with status FAILED.
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。