.agents/skills/scout-migrate-from-ftr/references/generate-plan.md
Detailed step-by-step instructions for the Plan step of the parent skill (SKILL.md). Produce a single Markdown file capturing every architectural decision the executor needs.
The plan answers what and why; execute-plan.md answers how.
loadTestFile, configs, services, and page objects while analyzing the source suite.Before starting, collect or confirm:
x-pack/platform/test/functional/apps/dashboard).x-pack/platform/test/functional/config.base.ts). Walk up from the test directory if not provided.x-pack/platform/plugins/shared/dashboard). If unknown, infer from the plugin that owns the FTR tests.Write the plan to migration-plan-<source-dir-slug>-<YYYY-MM-DD>.md in the target Scout module root, where <source-dir-slug> is the basename of the FTR source directory and the date is the day the plan was generated. Examples:
x-pack/platform/test/functional/apps/painless_lab → migration-plan-painless_lab-2026-05-06.mdx-pack/solutions/observability/test/api_integration/apm → migration-plan-apm-2026-05-06.mdThe shared migration-plan- prefix makes plans easy to identify and group; the source-dir slug + date together prevent collisions across multiple migrations under the same plugin while still allowing intentional re-runs on the same source/day to overwrite cleanly.
Go deep on FTR analysis (read every file, understand every pattern, flag every issue). Stay at the decision level for Scout — the executor step already knows Scout's APIs, fixtures, and patterns. The planner's job is to say what was found and what should happen, not how to implement it in Scout code.
Recursively read the FTR directory. For each file, record:
index (has loadTestFile), test (has describe/it), config, page_object, service, helper, archive, or fixtureit(...) / describe(...) blocksAlso read thoroughly:
kbnTestServer.serverArgs, esTestCluster.serverArgs, security.roles, security.defaultRoles, apps, testFiles, and any services/pageObjects registrations. Note config inheritance chains (which base config does it extend?).index.ts file that uses loadTestFile: capture shared before/after hooks and their setup logic. Note what state they create and whether downstream tests depend on it.existOrFail, missingOrFail, expect inside helpers).For every test file, decide UI test / API test / unit test (RTL/Jest) / drop / defer using the criteria in pick-correct-test-type.md. For each decision, write a one-line justification.
File splitting: when a single FTR file tests multiple roles or unrelated flows, recommend splitting it into separate specs (one role + one flow per file). List the proposed splits.
Rate each test file:
it blocks, no custom roles, no archives, straightforward page objectsit blocks, custom roles, archive loading, moderate page object usageSort the full inventory by complexity (simple first).
For each test (or group), decide whether it can run in parallel or must run sequentially:
Document which tests can share the same parallel pool and which need isolation. Explain why, referencing the specific state or mutation that drives the decision.
For each archive, data fixture, or setup pattern found:
kibanaServer.uiSettings.replace, uiSettings.update, uiSettings.delete call, which tests use them, and whether they use replace-all semantics (wipes all settings) vs selective setsuperuser or the FTR default role when a narrower role would suffice. For each, note what privileges the test actually exercisesviewer → privileged/editor → admin) unless the test specifically validates permission-scoped behavior (e.g. a user limited to index X can see Y but not Z) that no built-in role expresses — only then keep a custom role. Many FTR suites scoped custom roles needlessly; flag those as candidates for a default role rather than a 1:1 custom-role port.run_as, API-key-based auth, certificate auth, or any non-standard FTR authThe execution step picks the specific Scout auth method (loginAsViewer, loginWithCustomRole, etc.); the plan provides the role inventory, privilege definitions, usage context, and the default-vs-custom recommendation.
existOrFail, missingOrFail, or expect calls that should move to specs)test/scout trees? Note: exists / exists-but-in-wrong-scope / missingfind.byCssSelector(...), find.byClassName(...), or text-based lookups. Note where data-test-subj attributes are missing in source code and need to be addedPrefer Scout's default servers config. It mirrors the Elastic Cloud (MKI/ECH) setup and is batched with other default-config suites in CI, so tests that pass locally on it are likely to pass on Cloud. A custom server config set runs only in local pipelines (no Cloud) and adds CI cost — treat it as a last resort, used only when a setting must be present at Kibana boot (e.g. registering HTTP routes at plugin setup). See Can your tests reuse Scout's default servers config?.
kbnTestServer.serverArgs and esTestCluster.serverArgs across all relevant configs (including inherited base configs)apiServices.core.settings() runtime feature flags, the uiSettings fixture, or data ingestion rather than a custom configsrc/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/Scout is deployment-agnostic — the goal is "write once, run locally and on Elastic Cloud." Reference Design tests with a cloud-first mindset for the underlying principles.
For each test group, answer all four:
@skipServerless, @skipStateful, etc.) and .buildkite/ftr-manifests/ftr_*_configs.yml so CI coverage is preserved.src/platform/**, x-pack/platform/**): use tags.deploymentAgnostic when the original intent was "run everywhere."x-pack/solutions/observability|security|search/...): use explicit tags.stateful.* + tags.serverless.<solution>.* rather than tags.deploymentAgnostic.localhost URLs or local file pathsScan every test file for patterns that need attention during migration:
| Smell | What to flag |
|---|---|
| try/catch swallowing errors | try { ... } catch { } or catch blocks that don't rethrow |
| Conditional test logic | if/else inside it() blocks that change assertions based on runtime state |
| Global loading indicator waits | waitForSelector('globalLoadingIndicator') or similar global spinners |
| Hardcoded timeouts | await new Promise(r => setTimeout(r, ...)), browser.sleep(...) |
| Shared mutable state | Variables mutated across it() blocks relying on execution order |
Sequential journey as separate it blocks | Multiple it() blocks that form a single user journey (shared browser state) |
| Duplicate test cases | Multiple it() blocks testing the same behavior with minor variations |
| Missing cleanup | Setup in before/beforeEach without corresponding teardown |
| Retry wrappers | retry.try(...), retry.waitFor(...) around assertions |
| UI-based setup/teardown | before/after hooks that navigate pages or click through UI to create/delete test data instead of using APIs |
| Onboarding/tour dismissals | browser.setLocalStorageItem(...), manual tour-dismiss clicks, getting-started bypasses |
| Brittle CSS selectors | find.byCssSelector(...), find.byClassName(...), text-based lookups without data-test-subj |
| Over-privileged execution | Tests running as superuser that don't need elevated privileges |
For each smell, note the file and relevant context.
Group tests into ordered batches that can be migrated independently:
For each batch:
autopilot (executor can handle end-to-end), guided (needs a few human decisions or source code changes), or hands-on (significant manual work or missing infrastructure)data-test-subj in source code)Output the plan to migration-plan-<source-dir-slug>-<YYYY-MM-DD>.md (see Output filename convention above) in the target Scout module root. Follow plan-template.md exactly for section structure, table formats, and bullet styles so the executor and any reviewers can parse sections programmatically.
NEEDS VERIFICATION rather than guessing.plan-template.mdpick-correct-test-type.mddocs/extend/scout/best-practices.md#design-tests-with-a-cloud-first-mindset