# Migration Assistant — Improvement & Growth Plan

> Package: capell-app/migration-assistant · Kind: package · Tier: premium · Product group: Capell Operations · Bundle: operations · Status: Complete

## 1. Snapshot

Migration Assistant is the generic Capell content-migration framework: it owns Recovery Center page import (upload → review → relation resolution → dry-run validation → queued execution → status polling) plus deterministic ZIP package export/import, CSV/XML source readers, a relation-resolver chain, media ingest, and rollback reports. Surfaces are `admin` + `console`; the working admin entry points are `ImportPagesPage` (`recovery-center/import-pages`) and `ImportSessionResource`, driven by Actions under `src/Actions/Imports` (`StartPageImportAction`, `AdvancePageImportToValidationAction`, `DispatchPageImportAction`, `RefreshPageImportStatusAction`) and `ExecuteImportPlanJob`. Owned tables are `import_sessions` and the rollback-report table; deps are `capell-app/admin`, `capell-app/core`, `lorisleiva/laravel-actions`, `spatie/laravel-package-tools`. It is the **framework**; `capell-app/wordpress-importer` is a **consumer** — wordpress-importer `require`s migration-assistant and registers a `WxrReader` into `ImportSourceRegistry` (`wordpress-importer/src/Providers/WordPressImporterServiceProvider.php:26-28`), so WordPress parsing stays out of this package.

Current marketplace summary (verbatim, `capell.json` → `marketplace.summary`):

> "Safely move pages and media into Capell — preview every change, validate before you write, and keep a rollback report for every import."

Screenshot coverage is reopened: `capell.json` → `marketplace.screenshots` now keeps only the extension card because visual audit showed the promoted workflow images were mostly empty Import Batches tables or a non-Capell mock. The committed PNGs remain runner evidence until seeded import sessions produce distinct validation, relation-resolution, rollback, and export states.

## 2. Improvements (existing functionality)

Prioritized.

1. **Done/Shipped: create real `SiteImport` sessions and run them through the admin wizard.** `StartSiteImportAction` reuses the reviewed page-import wizard state path with `ImportSessionKind::SiteImport`, `StartPageImportAction` guards package type so page imports reject `site-export` archives, and `ImportSitesPage` now extends the working wizard UI with site-import upload, review, relation resolution, dry-run validation, dispatch, and status polling. Evidence: `PageImportWorkflowActionsTest` covers site-export upload state moving to review with a `SiteImport` session and page-import rejection of site-export archives; `ImportSitesPageTest` covers UI → `SiteImport` kind → queued `ExecuteImportPlanJob`. — `src/Actions/Imports/StartPageImportAction.php`, `src/Actions/Imports/StartSiteImportAction.php`, `src/Filament/Pages/ImportPagesPage.php`, `src/Filament/Pages/ImportSitesPage.php`, `src/Actions/Imports/ResolvePageImportSessionAction.php`, `tests/Admin/Feature/Actions/Imports/PageImportWorkflowActionsTest.php`, `tests/Feature/Admin/ImportSitesPageTest.php` — **L**

2. **Done/Shipped: fixed the corrupted rollback-report table name across the codebase.** The physical table, model `$table`, provider migration registration, README/overview docs, and rollback-report health check now use `import_rollback_reports`. The create migration and the June 4 compatibility migration both detect the old malformed `import_rollback_dashboard-dashboard_reports` table and rename it safely for existing installs. — `database/migrations/`, `src/Models/ImportRollbackReport.php`, `src/Providers/MigrationAssistantServiceProvider.php` — **M**

3. **Done/Shipped: repaired garbled prose left by the same find/replace.** The collision-detector contract and review-row builder now describe the null detector as reporting no collisions, while the overview/workflow docs consistently refer to Migration Assistant. The package search surface no longer exposes the corrupted prose tokens in source, docs, manifest, or migration compatibility code. — `src/Contracts/PageCollisionDetector.php`, `src/Actions/BuildPageReviewRows.php`, `docs/overview.md`, `docs/import-export-workflow.md`, `database/migrations/` — **S**

4. **Done/Shipped: implemented manifest-keyed health probes.** `MigrationAssistantHealthCheck::runDiagnostics(?string $key)` now returns translated targeted diagnostics for the three current manifest health rows: package-reader readiness, rollback-report support, and media ingest limits. Evidence: `MigrationAssistantHealthCheckTest` covers keyed diagnostics, unknown-key fallback, source-reader readiness, and invalid package-size limits failing the package-reader check. — `src/Health/MigrationAssistantHealthCheck.php`, `resources/lang/en/imports.php`, `tests/Feature/Admin/MigrationAssistantHealthCheckTest.php`

5. **Done/Shipped: reconciled the admin wizard query budget claim.** `capell.json` now declares `performance.adminQueryBudget: 120` for the multi-step Livewire wizard instead of the older unverified `40`, and the package manifest test pins that budget so future metadata changes are deliberate. — `capell.json`, `tests/Unit/MigrationAssistant/PackageManifestTest.php` — **S**

6. **Done/Shipped: removed orphan `ImportSessionKind` cases.** `ImportSessionKind` now exposes only the package-owned archive workflows, `PageImport` and `SiteImport`; WordPress and spreadsheet ingestion stay on the source-reader extension path rather than reserving session kinds this package does not route. Evidence: `ImportSessionKindTest` locks the enum values to `page-import`/`site-import` and asserts `WordPressImport`/`SpreadsheetImport` are not part of the contract. — `src/Enums/ImportSessionKind.php`, `tests/Unit/MigrationAssistant/ImportSessionKindTest.php` — **S**

7. **Make `ExecuteImportPlanJob` retry-aware (`tries = 1`).** — The job sets `public int $tries = 1` (`src/Jobs/ExecuteImportPlanJob.php`), so any transient failure (lock contention, disk hiccup) is terminal. Media ingest is explicitly engineered to be idempotent and runs outside the import transaction, and `WithoutOverlapping(...)->dontRelease()` already guards concurrency — the design anticipates retries the config forbids. Raise `tries` (with backoff) now that the work is idempotent, or document why single-shot is deliberate. — `src/Jobs/ExecuteImportPlanJob.php` — **S**

## 3. Missing Features (gaps)

Tied to `capell.json` → `capabilities` (`migration-assistant`, `migration-assistant-admin`, `migration-assistant-console`) and migration-tool norms.

- **Done/Shipped: console surface now has status, rollback-report audit, rollback execution, and headless package export/import commands.** `MigrationAssistantServiceProvider` registers `migration-assistant:export`, `migration-assistant:import`, `migration-assistant:status`, `migration-assistant:rollback-report`, and `migration-assistant:rollback-execute`, and `capell.json.commands` exposes all five. The import command creates and validates sessions through the same Actions as the admin wizard, then optionally queues or synchronously executes the job for controlled maintenance windows. — `src/Console/Commands/ExportMigrationAssistantPackageCommand.php`, `src/Console/Commands/ImportMigrationAssistantPackageCommand.php`, `tests/Feature/Admin/MigrationAssistantConsoleCommandsTest.php`
- **Done/Shipped: automated rollback execution.** `ExecuteImportRollbackAction` deletes records listed in `created_models` in reverse creation order, skips missing rows, skips rows edited after the import execution timestamp, supports dry-run, and records an execution summary back onto the rollback report. `migration-assistant:rollback-execute` exposes the same behavior from console with JSON output. — `src/Actions/ExecuteImportRollbackAction.php`, `src/Console/Commands/ExecuteMigrationAssistantRollbackCommand.php`, `tests/Integration/MigrationAssistant/ExecuteImportRollbackActionTest.php`, `tests/Feature/Admin/MigrationAssistantConsoleCommandsTest.php`
- **Field-mapping UI (table stakes).** `FieldMapper` (56 lines) and `ImportTargetRegistry` exist, but mapping is code-level; there is no column→field mapping screen in the wizard. Flat-file (CSV/XML) importers normally let an operator map source columns to target fields visually.
- **Resumable / chunked import for large datasets (table stakes at scale).** `PageImportService::import()` wraps the entire payload in a single `DB::transaction` (`src/Services/Import/PageImportService.php`) and the job has a 900s timeout. There is no batching, checkpointing, or progress-by-row — a large site will hit memory/timeout limits with no way to resume.
- **Redirect / URL preservation reporting (differentiator for SEO).** PageUrl rows are restored (`restorePageUrls`), but there is no surfaced report of preserved vs dropped URLs/redirects — a natural cross-sell hook into `url-manager` / `seo-suite`.
- **Validation report export.** `BuildImportValidationSummaryAction` produces an in-wizard summary but there is no downloadable/persisted validation artifact for sign-off before execution.
- **Source connectors beyond flat files (differentiator).** Only CSV + XML readers ship (`ImportSourceRegistry`). Direct DB/JSON/sitemap connectors would broaden the funnel; the registry is built for exactly this extension.

## 4. Issues / Risks

- **Done/Shipped: manifest capabilities are reachable through package-owned surfaces.** The console surface ships status, export, import, rollback-report, and rollback-execute commands (§3), and the dedicated site-import admin page now runs the same review/resolve/validate/dispatch workflow as page imports while preserving `ImportSessionKind::SiteImport`.
- **Done/Shipped: health checks now run concrete probes.** `MigrationAssistantHealthCheck` verifies package-reader dependencies/limits/source readers/manifest validation, rollback-report table and morph alias support, and positive media ingest limits with translated Diagnostics output. Remaining marketplace risk is screenshot quality, not no-op diagnostics or unreachable package-owned capabilities.
- **Single-transaction import = large-dataset memory/time risk.** Entire `payload` decoded and written inside one transaction (`src/Services/Import/PageImportService.php`); combined with `tries = 1` and a 900s timeout, a big import that fails mid-way rolls back wholesale with no resume (§2.7, §3).
- **Done/Shipped: structured partial-failure details.** `ImportExecutionReport` now preserves the legacy flat `errors` list and also exposes `structured_errors` entries with `entry`, `phase`, and `message`, populated by `PageImportService` for page-write and owned-relation failures and carried through site imports. `ExecuteImportPlanJob` still keeps the short `failure_reason` summary for list views, while the stored `result_summary` contains structured entries for drill-down/automation.
- **Test gaps on the highest-risk paths.** Strong coverage exists for readers, manifest validation, size-limit enforcement, resolvers, page import, site-import wizard dispatch, rollback report creation/execution, DTO contracts, and health-check probes. Remaining gaps: no browser screenshot runner proof for seeded validation/relation-resolution/rollback/export states, and large dataset resumability remains future work.
- **Cache/public-output safety: low risk, confirmed.** `capell.json` → `performance.cacheSafety.cacheable=false`, `sensitiveOutput=false`, and the package emits no anonymous frontend output (`frontendRenderBudgetMs: 0`). Import-session detail is admin-gated via `ImportSessionPolicy` + `HasPageShield`. No public-leak surface found.
- **Performance budgets unverified.** `adminQueryBudget: 40` has no enforcing test (§2.5); `cacheTags: []` is consistent with a non-cacheable admin tool.
- **i18n is solid.** 73 `__()` calls, all namespaced under `capell-admin::exchanger.*` (e.g. `import_pages`, `cancel_session_confirm_body`); no hardcoded user-facing strings observed in the wizard. The only English-literal strings are the rollback **manual instructions** in `CreateImportRollbackReportAction::instructionsFor()` — these are persisted operator guidance and should be translatable.
- **Documentation corruption mostly cleared.** The prose artefacts called out in §2.3 are repaired across published README/docs and the boost skill. The remaining table-name cleanup tracked in §2.2 is a compatibility/migration concern, not buyer-facing prose.

## 5. Marketplace & Selling

**Critique.** Marketplace copy has been rewritten around the buyer outcome — moving existing pages and media into Capell with preview, validation, queued execution, and rollback reports. Remaining marketplace risk is no longer the headline copy, health probes, rollback table naming, rollback execution, console reachability, or site-import wizard reachability; it is screenshot quality.

**Improved 1-sentence summary:**

> Safely move pages, sites, and media into Capell — preview every change, validate before you write, and keep a rollback report for every import.

**Improved 3–4 sentence description:**

> Migration Assistant is Capell's content-migration engine: upload a content package or flat file (CSV/XML), review and resolve incoming relations, run a dry-run validation, then execute on a queue with live progress. Every run produces a rollback report capturing exactly which records were created, so nothing is a one-way door. It is also the foundation other importers build on — the WordPress Importer plugs straight in — making it the first thing you install when bringing an existing site onto Capell. Media is deduplicated by checksum and large payloads are size-guarded, so imports stay safe and idempotent.

**Reopened: screenshot/media reconciliation.** The manifest now exposes only the extension card. Recapture seeded, styled Capell import workflow states before promoting workflow screenshots again.

**Pricing / tier / bundle positioning.** Tier `premium`, bundle `operations`, license `paid`. Migration tools are **acquisition drivers, not revenue centers** — they are how a prospect's existing content lands inside the platform, after which every other package monetizes. Consider whether the _page-import_ core belongs in a lower/free tier (or a time-limited "import window") to maximize funnel, keeping site-import, automated rollback, and console/CI commands as the premium upsell. Bundling it into `operations` is defensible, but a "Migration & SEO" cross-sell bundle (below) may convert better.

**Cross-sell via deps + Extension Suites.** Hard dependency edge already exists: `wordpress-importer` → `migration-assistant`, so a "coming from WordPress?" funnel should surface both. Natural Extension-Suite pairings: **wordpress-importer** (source connector), **url-manager** (preserve/redirect imported URLs — see §3 redirect reporting), **seo-suite** (post-import metadata/redirect hygiene), and **media-library** + **diagnostics** (already listed in "Best Used With"). Lead the listing with these.

**Differentiators / value props / target buyer.** Differentiators: dry-run validation _before_ any write, deterministic checksum-verified packages, idempotent checksum-keyed media reuse, a persisted rollback report per import, and an open `ImportSourceReader` registry (any vendor can add a source). Target buyer: agencies/teams migrating an existing site (especially WordPress) onto Capell, and ops teams moving content between Capell environments.

**Keywords/tags (8–12):** `migration`, `import`, `export`, `wordpress-migration`, `content-migration`, `csv-import`, `xml-import`, `site-import`, `rollback`, `dry-run-validation`, `media-migration`, `cms-migration`.

## 6. Prioritized Roadmap

| Item                                                                                                                                                                                                                                                                                                                  | Bucket | Effort | Impact | Section ref |
| --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | ------ | ----------- |
| Done/Shipped: Fix corrupted `import_rollback_*` table name (migration + model + provider + docs)                                                                                                                                                                                                                      | Done   | M      | High   | §2.2        |
| Done/Shipped: Repair garbled Migration Assistant prose in src + docs                                                                                                                                                                                                                                                  | Done   | S      | Med    | §2.3        |
| Recapture seeded, styled Capell workflow screenshots before promoting Migration Assistant product media. Evidence: empty Import Batches and non-Capell mock captures were demoted from `capell.json`; the code path is now ready but product media still needs runner captures.                                       | Later  | S      | High   | §5          |
| Done/Shipped: Rewrite marketplace summary + composer description (buyer-facing). Evidence: manifest and composer now lead with safe page/media migration, preview, validation, queued execution, and rollback reports.                                                                                                | Done   | S      | High   | §5          |
| Done/Shipped: Implement real health-check probe methods. Evidence: `MigrationAssistantHealthCheckTest` covers manifest-keyed package-reader, rollback-report, and media-ingest diagnostics.                                                                                                                           | Done   | M      | High   | §2.4, §4    |
| Done/Shipped: Resolve manifest-vs-reality enough to create and dispatch `SiteImport` sessions from site-export packages. Evidence: `StartSiteImportAction` starts the site-import review state, package-type guards reject wrong archive kinds, and `ImportSitesPageTest` covers UI → `SiteImport` kind → queued job. | Done   | M      | High   | §2.1, §4    |
| Done/Shipped: Removed orphan `WordPressImport`/`SpreadsheetImport` kinds. Evidence: `ImportSessionKind` contains only `PageImport`/`SiteImport`, with `ImportSessionKindTest` covering the contract.                                                                                                                  | Done   | S      | Med    | §2.6        |
| Done/Shipped: status, rollback-report, rollback-execute, headless export, and headless import commands exist                                                                                                                                                                                                          | Done   | M      | High   | §3          |
| Done/Shipped: Build site-import wizard end-to-end (UI → SiteImport kind → job). Evidence: `ImportSitesPage` now specializes the shared import wizard with `StartSiteImportAction`, and `ImportSitesPageTest` covers upload, review, resolve, validate, dispatch, session kind, and queued job.                        | Done   | L      | High   | §2.1, §3    |
| Ship automated one-click rollback execution from `created_models`                                                                                                                                                                                                                                                     | Done   | M      | High   | §3          |
| Shipped 2026-06-06: Raise `ExecuteImportPlanJob` `tries`/backoff now that ingest is idempotent                                                                                                                                                                                                                        | Done   | S      | Med    | §2.7        |
| Done/Shipped: Add query-budget + structured partial-failure tests; enforce `adminQueryBudget`                                                                                                                                                                                                                         | Done   | M      | Med    | §2.5, §4    |
| Add visual field-mapping UI (CSV/XML column → target field)                                                                                                                                                                                                                                                           | Later  | L      | Med    | §3          |
| Chunked/resumable large-dataset import with checkpointing                                                                                                                                                                                                                                                             | Later  | L      | High   | §3, §4      |
| Redirect/URL-preservation report + url-manager/seo-suite cross-sell hooks                                                                                                                                                                                                                                             | Later  | M      | Med    | §3, §5      |