# Notes for WordPress.org reviewers — MigratePilot v1

Author: Sibusiso Dev. Company credit: AfriBiz Tech. Product documentation: https://migratepilot.sibusiso.dev/migration-guide.html.

This is an administrator-operated, single-site migration utility. It intentionally replaces destination content and database tables after package validation, explicit backup/trust acknowledgements, and exact destination-host confirmation. It retains the destination's own installed MigratePilot code. It is not a remote file editor, code downloader, telemetry client, or background cloud service.

## Native filesystem operations

The importer/exporter requires bounded reads and writes, seekable binary streams, flock locks, private 0700/0600 permissions, truncation to committed offsets, and same-volume atomic rename for staging/rollback. WP_Filesystem transports do not expose these stream, locking, and commit semantics. Native operations are therefore intentional and annotated locally with specific PHPCS codes; authorization, input validation, safe HTTP validation, and database checks are not globally disabled.

Temporary packages and operational state are stored outside the public document root. Imported uploads/themes/plugins are staged on the destination content volume and swapped only after confirmation. The plugin directory-write checker may recognize these swaps: they restore the incoming site's explicitly approved plugins/themes, rather than storing operational data in MigratePilot's own plugin folder. Symlinks, traversal, invalid path components, and attempts to replace this migration plugin are rejected.

## Output and exception handling

Authenticated package downloads intentionally echo binary bytes under application/octet-stream or application/zip with nosniff, Content-Length and range handling. Applying HTML escaping would corrupt those bytes. Logs use application/json with nosniff. Runtime exception messages are returned through wp_send_json_error and rendered using textContent; HTML error pages escape the message at the output sink. Exception constructors are annotated rather than HTML-encoding their JSON messages.

## Database operations

Routine table inspection, staged options, and frame receipts use wpdb::prepare with %i/%s/%d placeholders. Migration also needs schema inspection, DDL, transactions, bulk table renames, binary-safe row insertion, and uncached reads across changing staging tables. Identifiers are backtick-quoted with embedded backticks doubled; streaming values use hex literals (or SQL NULL), and collation/charset names are restricted before interpolation. CREATE statements originate in a trusted package and are constrained to the declared table/schema; unsupported database features are rejected. Dynamic query helpers and keyset queries should be reviewed in this migration-specific context rather than converted to cached option APIs or string value placeholders for complete SQL statements.

## Direct site-to-site endpoint

The nopriv HTTPS POST endpoint is intentionally available to another server with a short-lived 256-bit bearer code; there is no public destination-write endpoint. The source stores only a SHA-256 hash of the secret. The grant is scoped to one completed export, expires within one hour, and is revocable. Requests return only package metadata or one verified block. Secrets are sent in POST bodies, not query strings. Destination replacement remains administrator/nonce protected.

## Retention and deletion

Packages normally expire after 24 hours of inactivity, activity after 30 days. Cleanup is bounded, cron-driven and also runs when creating jobs; low traffic or disabled cron can delay deletion. Recovery evidence is preserved on ambiguous interrupted commits. Deactivation stops scheduled cleanup; uninstall does not silently destroy private recovery evidence. These limitations are disclosed in readme.txt and the guide.

## Licensing and external services

Original code/artwork: GPL-2.0-or-later, LICENSE.txt. Manrope: SIL OFL 1.1, assets/fonts/OFL.txt; original TTF and WOFF2 included, locally hosted. No analytics tag, ad code, developer API, updater, or externally hosted font is bundled. Direct transfer calls only the source site supplied by the administrator. See readme.txt for the privacy/data disclosure.

## Validation scope

The regression suite uses an isolated disposable MySQL server and real WordPress database APIs, with a simulated HTTP transport for direct transfer. It is not a substitute for complete hosted two-site tests, independent security review, or a full PHP/WordPress compatibility matrix. Development tests and browser fixtures are excluded from the submission ZIP. No claim of WordPress.org approval, perfect recovery, or infinite hosting capacity is made.

## Naming advisory

The submission name and intended directory slug are MigratePilot / migratepilot. The WP prefix was removed after the submission form rejected it. The distribution folder and translation domain are migratepilot; historical PHP class names, admin page slugs, nonce actions and private checkpoint paths retain their existing prefixes for compatibility. Deactivate the old wp-migratepilot installation before activating the new directory installation; do not run both copies together.


## Serialized compatibility recovery

New .wmp imports catch a structured value-level exception before final replacement. Only table/column, package row ordinal, original class, an immutable cell fingerprint, and an explicitly inferred plugin name are exposed to the authorized administrator; raw setting contents are not stored in diagnostics. Plugin names come from static package headers read during validation, never included/executed files.

The repair parser reads serialized tokens and adjusts supported text byte lengths without unserialize, class loading, constructors, wakeup hooks, or property-name changes. It rejects opaque Serializable payloads, references, enums, malformed/trailing bytes, duplicate keys and excessive nesting/work. Enum tokens are intercepted before PHP unserialize can invoke an enum autoloader. Keeping a value unchanged preserves the original bytes and may retain source URLs; it does not remove a row or disable a plugin.

Each decision requires the existing administrator capabilities and API nonce, a paused resumable import, exact issue fingerprint, available repair mode, and explicit acknowledgement. Choices are stored atomically in private job storage under the job lock, scoped to one cell by default, with operator/time recorded. An explicit matching scope stores an import-local rule keyed by the exact original class, source table and column. Every matching repair still passes the parser; unsupported values pause for review. Different classes, tables, columns and future imports do not inherit the rule. Import frame receipts and transaction rollback retain their existing replay protections. The tests exercise actual staged repair, preservation, final commit, stale IDs, missing acknowledgements, unavailable repairs, and absence of wakeup/enum autoload callbacks.

## Error reporting and diagnostic warnings

The plugin leaves PHP error_reporting and display_errors under the host and WordPress configuration; it does not change these settings during migration. Production sites should configure error display separately from server logging. Limited error_log messages retain evidence of scheduled cleanup and rollback failures. Removing them solely to satisfy a development-function warning would reduce recovery diagnostics. Worker time limits remain bounded and explicit.

The uploaded chunk temporary filename is not treated as trusted input: it must be a string from a successful upload, pass is_uploaded_file, and fit the negotiated block-size limit before it is read. It is never moved to an executable location or used as a package extraction path. The direct-transfer endpoint uses short-lived bearer authorization instead of a browser-session nonce; an arbitrary nonce would break authorized server-to-server reads. The destination import API still requires administrator capabilities and its WordPress nonce.

1.0.0 review additions
Streaming validation spools each row in private job storage and calls the same non-instantiating compatibility transformer used during import. Group counts contain only class/table/column and an opaque cell ID, never values. Decisions remain behind the existing administrator/nonce guard, job lock, explicit acknowledgement, exact issue ID and import-local group scope. Confirmation rejects unresolved groups. The import parser checks every repaired value again.
Destination verification runs after commit in the same request without executing incoming plugins or fetching URLs. It reads URL/theme/plugin settings and checks files; failures are reported as needs-review, never used to retry a committed replacement. The HTML report uses DOM textContent and contains no stored settings or secrets. Recovery preparation is guidance, not an automatic or retained backup.

version 1 backup review notes
- Backups use administrator-only nonce-protected API and downloads. Cron rechecks the configuring user capabilities before each quantum.
- Private storage must resolve outside ABSPATH, WP_CONTENT_DIR and the server document root; a salt/path-bound owner marker prevents imported configuration being used at a different installation. Administrators must choose storage that is not exposed through another vhost or alias.
- Native flock, 0700 mkdir, 0600 checkpoint permissions and same-volume atomic rename are required. WP_Filesystem remote transports do not offer these locking/atomic guarantees. Specific call-site annotations retain this requirement.
- Exception text is JSON data rendered with textContent; HTML download failures use esc_html. Escaping stored messages early would double-encode the UI.
- Backup worker failures log only a fixed diagnostic, never archive contents or credentials.
- Local unencrypted archives are verified before becoming eligible for downloads. Failed copies never replace successful ones. Count/age retention preserves the newest success and protected copies; failed partial work expires after seven days.
- Live backups are not cross-request transaction snapshots. No automatic schedule is enabled on activation. Core/configuration files, remote storage and incremental backup are outside scope.


BACKUP RELIABILITY ADDITIONS (v1 development)
Google Drive is explicitly opt-in and marked preview pending live-account validation. Credential files and upload checkpoints use AES-256-GCM with installation-bound keys, authenticated filenames, private storage, atomic replacement and locking. OAuth state expires, is user-bound and consumed before exchange. Only fixed Google HTTPS endpoints are used; redirects are disabled and responses bounded. Session URLs are validated before bearer credentials are sent. Chunk offsets come from the server and are bounded. A connection change cannot resume another account's upload. No remote files are shared or deleted. Pending/failed remote copies block automatic local retention.
The WordPress HTTP API is used for remote requests. Native filesystem operations are necessary for locks, seeking archive chunks and atomic encrypted checkpoint writes in the existing private-storage subsystem. Exceptions are rendered using textContent in the admin UI, or escaped by the callback error sink.
Alerts are opt-in and throttled. Rehearsals are explicitly administrator-reported and reject production-host URLs. No restore is automatically executed by the checklist.

23 October 2026 review response audit
All browser mutations require POST, the migration capability checks and a migratepilot_api nonce before command processing. Downloads/logs check capabilities and an action/job-bound nonce. Welcome dismissal checks POST/capabilities/nonce. Google OAuth completion requires current capabilities, HTTPS and user-bound, expiring single-use state. The public transfer endpoint intentionally uses HTTPS POST and a 256-bit, hashed, expiring bearer grant scoped to one completed export; it does not use browser cookies. Grant creation requires an administrator nonce, and serving rechecks the granting user permissions. Legacy grants without a user binding fail closed. New actions, nonce names, cron events and localized JS configuration use migratepilot_ prefixes. Legacy cleanup events are removed on admin initialization/deactivation. Install this build on both ends of direct transfers and create a new connection code. Existing Google clients must use the redirect URI shown in Backups (action=migratepilot_drive_callback).


Directory review response — 29 October 2026

Content root labels in archives (uploads/themes/plugins) are logical names, not assumptions about the destination filesystem. Uploads use wp_upload_dir() basedir/baseurl, themes use get_theme_root(), and the plugin root comes from plugin_dir_path(WP_MIGRATEPILOT_FILE). Main-file includes use the same plugin API. Relocated roots are used consistently in inventory, legacy ZIP export/import, streaming export/import, verification, staging, commit and rollback. Destination upload_path/upload_url_path options are preserved. New archives carry root metadata for database path rewriting; older archives retain standard-root fallback compatibility.

Nested roots, multiple theme roots, symlink/aliased paths and invalid locations fail closed. Export/import checkpoints bind to the resolved directories and cannot silently resume into a different location. Private storage validation also rejects all resolved public content roots, including uploads outside the core directory.

Migration-specific exceptions requested for reviewer consideration:
- ABSPATH is retained in one documented helper for the exact WordPress core .maintenance location, stable installation ownership/locks, exclusion checks and source-path metadata. get_home_path() is separately used for public-root exclusion; it can differ when core is in a subdirectory and must not replace ABSPATH for core maintenance.
- WP_CONTENT_DIR is retained in one documented helper solely for source/destination metadata and public-storage exclusion. There is no filesystem counterpart to content_url(). It is no longer used to construct uploads/themes/plugins locations.
- Packages, database rows, credentials and recovery journals remain in validated private storage outside public roots. They are not ordinary public media; moving unencrypted database archives into uploads would expose sensitive data on servers that do not enforce .htaccess. Please confirm this migration-specific storage exception is acceptable.
- Incoming/rollback site trees remain siblings of their API-resolved live directories for same-filesystem atomic renames. This is explicitly confirmed replacement of site content, not runtime data written in the plugin folder. Staging inside uploads would nest the staging area inside a directory being replaced and can break rollback or cross filesystems. Please confirm this requirement is acceptable for migration tools.

Directory tests exercise actual file replacement and database failure rollback in a disposable WordPress environment with WP_DEBUG enabled. Google Drive and email remain mocked, and no live hosted migration or directory approval is claimed.

Main-file rename — 30 October 2026
The runtime entry point is migratepilot/migratepilot.php. Legacy wp-migratepilot.php activation entries remain only in the import compatibility list so imported databases cannot reactivate a missing old entry point. Existing pre-directory installations must deactivate the previous build, replace its files and reactivate MigratePilot. No legacy bootstrap file is distributed.
