Skip to content

Migration Assistant — Improvement & Growth Plan

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

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 requires 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.jsonmarketplace.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.jsonmarketplace.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.

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.phpL

  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.phpM

  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.phpS

  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.phpS

  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.phpS

Tied to capell.jsoncapabilities (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.
  • 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.jsonperformance.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.

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-importermigration-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.

ItemBucketEffortImpactSection ref
Done/Shipped: Fix corrupted import_rollback_* table name (migration + model + provider + docs)DoneMHigh§2.2
Done/Shipped: Repair garbled Migration Assistant prose in src + docsDoneSMed§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.LaterSHigh§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.DoneSHigh§5
Done/Shipped: Implement real health-check probe methods. Evidence: MigrationAssistantHealthCheckTest covers manifest-keyed package-reader, rollback-report, and media-ingest diagnostics.DoneMHigh§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.DoneMHigh§2.1, §4
Done/Shipped: Removed orphan WordPressImport/SpreadsheetImport kinds. Evidence: ImportSessionKind contains only PageImport/SiteImport, with ImportSessionKindTest covering the contract.DoneSMed§2.6
Done/Shipped: status, rollback-report, rollback-execute, headless export, and headless import commands existDoneMHigh§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.DoneLHigh§2.1, §3
Ship automated one-click rollback execution from created_modelsDoneMHigh§3
Shipped 2026-06-06: Raise ExecuteImportPlanJob tries/backoff now that ingest is idempotentDoneSMed§2.7
Done/Shipped: Add query-budget + structured partial-failure tests; enforce adminQueryBudgetDoneMMed§2.5, §4
Add visual field-mapping UI (CSV/XML column → target field)LaterLMed§3
Chunked/resumable large-dataset import with checkpointingLaterLHigh§3, §4
Redirect/URL-preservation report + url-manager/seo-suite cross-sell hooksLaterMMed§3, §5