Phase 1 MVP: drop-in step registry + session + Markdown report

Plugin skeleton, drop-in step registry, option-backed session, single-page
checklist UI, downloadable Markdown report. Steps live as one file each
under includes/steps/ — adding/removing one is a single file change.
Step IDs are stable strings so renaming files preserves session data.

Architecture (hc-5ix.3): WPH_Steps singleton globs includes/steps/*.php,
natsort-orders by filename, requires each file (which returns a WPH_Step
instance), then applies a 'wph_steps' filter so installs can drop steps.

Session (hc-5ix.4): option-backed (per decision — plugin is installed
per-engagement, so DB-resident history would be lost on uninstall).
Single in-progress session per site; finished sessions render a report
that the user downloads/copies.

Recovery bootstrap (hc-5ix.1): detects whether wp-site-recovery is
installed + active, surfaces state on the start panel and in every
active session. Manual install for now; private update channel deferred
to hc-5ix.27.

Smoke-tested on testsite: registry discovery (13 steps in correct order),
start → update_step → progress count → finish → 8KB Markdown report →
discard cycle.
This commit is contained in:
2026-06-11 15:49:29 +01:00
parent d0d4d433d8
commit 8de7cad0de
23 changed files with 1160 additions and 0 deletions

View File

@@ -0,0 +1,19 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'before'; }
public function title(): string { return 'Before You Start'; }
public function blurb(): string {
return 'Confirm prerequisites before touching anything.';
}
public function sub_items(): array {
return [
'Confirm you have admin access to the WordPress dashboard and hosting control panel',
'Check the client record for any known issues, recent changes, or flags from the previous healthcheck',
'Note the current WordPress version, PHP version, and active theme before touching anything',
'Install our recovery plugin (Site Recovery) — done via the button below',
'Make a note of the recovery path in case there is a problem',
];
}
};

View File

@@ -0,0 +1,20 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'backup'; }
public function title(): string { return 'Step 1 — Take a Full Backup'; }
public function blurb(): string {
return 'Before any work takes place, take a complete backup manually. Do not rely on the most recent automated backup.';
}
public function sub_items(): array {
return [
'Back up both the database and all files (wp-content, wp-config.php, .htaccess)',
'Confirm the backup has completed and is accessible/downloadable',
'Note the backup location and timestamp in the client record',
];
}
public function escalation(): ?string {
return 'If the backup fails or cannot be confirmed, stop. Do not proceed until you have a verified backup.';
}
};

View File

@@ -0,0 +1,13 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'uptime'; }
public function title(): string { return 'Step 10 — Uptime and Availability'; }
public function sub_items(): array {
return [
'Check uptime monitoring logs if available — note any downtime incidents since the last healthcheck and flag to client if significant',
'Confirm the site is resolving correctly on both www and non-www',
];
}
};

View File

@@ -0,0 +1,19 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'small_fixes'; }
public function title(): string { return 'Step 11 — Small Fixes'; }
public function blurb(): string {
return 'Address any small issues found during the check that fall within the ~15 minute threshold. Anything longer goes on the quote list.';
}
public function sub_items(): array {
return [
'Broken internal links on key pages',
'Missing alt text on homepage images',
'Obvious content errors noticed in passing (broken shortcodes, missing widgets)',
'Reactivating a deactivated-but-needed plugin',
'Clearing accumulated spam comments',
];
}
};

View File

@@ -0,0 +1,16 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'wrap_up'; }
public function title(): string { return 'Step 12 — Wrap Up and Document'; }
public function sub_items(): array {
return [
'Log everything done in the client record: versions before and after, any issues found, any fixes applied, anything flagged for follow-up',
'Note the date, time taken, and technician',
'If using ManageWP or WP Umbrella, generate the client report and review it before sending — make sure it accurately reflects what was done',
'Send the client report or file it according to your process',
'If anything was flagged that needs a separate quote or client decision, send that communication now rather than leaving it',
];
}
};

View File

@@ -0,0 +1,19 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'environment'; }
public function title(): string { return 'Step 2 — Environment Check'; }
public function blurb(): string {
return 'Before touching updates, review the hosting environment.';
}
public function sub_items(): array {
return [
'PHP version — check against WordPress requirements and plugin compatibility. Flag if below 8.1. Note if EOL.',
'Disk usage — flag if over 80% used',
'Error logs — check the server error log and WordPress debug log if enabled. Note any recurring errors, 500s, or deprecated function warnings',
'wp-config.php — confirm WP_DEBUG is off on production',
'File permissions — spot check wp-config.php (should be 640 or 600), wp-content (755), uploads (755)',
];
}
};

View File

@@ -0,0 +1,21 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'core'; }
public function title(): string { return 'Step 3 — WordPress Core Update'; }
public function sub_items(): array {
return [
'Check current version against latest stable release',
'If an update is available, apply it',
'After update, load the site front-end and wp-admin and confirm both are functioning',
'Check the database upgrade prompt — if WordPress prompts to upgrade the database, run it',
'Note the version you updated from and to',
];
}
public function watch_outs(): array {
return [
'White screen of death post-update, admin redirect loops, missing admin menu items — these usually indicate a theme or plugin conflict with the new core version.',
];
}
};

View File

@@ -0,0 +1,25 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'plugins'; }
public function title(): string { return 'Step 4 — Plugin Updates'; }
public function sub_items(): array {
return [
'Go to Dashboard → Updates and review all pending plugin updates',
'Before updating, note which plugins have updates and what versions they are moving to',
'Update plugins one at a time if the site is complex or has many interdependencies; batch update is acceptable for straightforward sites',
'After each update (or after a batch), check the front-end and any key functional areas (forms, checkout, membership, etc.)',
'Check for any plugins that have been deactivated but not deleted — flag these to the client',
];
}
public function watch_outs(): array {
return [
'WooCommerce updates — always treat these as high-risk, test checkout flow afterwards',
'Page builder updates (Elementor, Divi, Beaver Builder) — can affect layout rendering',
'Security plugin updates — confirm they reactivate and are still reporting clean',
'Plugins that haven\'t been updated by their developer in over 12 months — flag as a risk',
'Plugins showing "Update unavailable" or removed from the WordPress repository — flag immediately, these can indicate abandoned or compromised plugins',
];
}
};

View File

@@ -0,0 +1,20 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'theme'; }
public function title(): string { return 'Step 5 — Theme Updates'; }
public function sub_items(): array {
return [
'Update the active theme if an update is available',
'If a child theme is in use (correct practice), the parent theme can be updated safely — confirm child theme is active',
'If no child theme is in use and the parent theme has been customised directly, do not update without flagging to the client first — the update will overwrite customisations',
'Update inactive themes only if they are legitimate fallback themes (e.g. a default Twenty* theme). Unused themes that serve no purpose should be flagged for removal',
];
}
public function watch_outs(): array {
return [
'Layout changes post-theme update, broken header/footer, missing custom fonts or colours — indicates customisation was done directly in the parent theme.',
];
}
};

View File

@@ -0,0 +1,23 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'visual'; }
public function title(): string { return 'Step 6 — Visual and Functional Check'; }
public function blurb(): string {
return 'Do a manual walkthrough of the site.';
}
public function sub_items(): array {
return [
'Homepage — load and visually inspect. Check for broken images, layout issues, console errors (open browser dev tools)',
'Navigation — click through the main menu. Confirm all links resolve correctly, no 404s on primary pages',
'Key pages — About, Contact, Services or equivalent. Check content renders correctly',
'Contact form — submit a test entry and confirm it delivers (check spam folder if no delivery). Note which form plugin is in use',
'If WooCommerce — check shop page loads, a product page loads, add to basket works. Do not need to complete a full test purchase every time unless flagged',
'If membership/login — confirm login page loads and (if test credentials available) login works',
'Mobile view — check the homepage and one internal page on a mobile viewport in browser dev tools',
'HTTPS — confirm the padlock is showing and there are no mixed content warnings',
'Redirects — confirm www/non-www and HTTP/HTTPS are redirecting correctly to the canonical URL',
];
}
};

View File

@@ -0,0 +1,16 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'performance'; }
public function title(): string { return 'Step 7 — Performance Check'; }
public function sub_items(): array {
return [
'Run a quick PageSpeed Insights check on the homepage',
'Note the scores (mobile and desktop) in the client record',
'Flag if mobile score has dropped significantly since last check (more than 10 points)',
'Check that caching is active — if using a caching plugin, confirm it is enabled and not throwing errors',
'Check image sizes on the homepage — flag if uncompressed images over 500KB are being served',
];
}
};

View File

@@ -0,0 +1,17 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'security'; }
public function title(): string { return 'Step 8 — Security Check'; }
public function sub_items(): array {
return [
'Confirm the SSL certificate is valid and not expiring within 30 days — flag if so',
'Check the WordPress user list — flag any unfamiliar admin accounts',
'Check for any recently modified core files if you have file change monitoring in place',
'Confirm the login URL is not the default /wp-admin if security hardening was previously applied',
'If a security plugin is active, review its dashboard for any flagged issues',
'Check that xmlrpc.php is disabled or restricted if not in use',
];
}
};

View File

@@ -0,0 +1,14 @@
<?php
if (!defined('ABSPATH')) exit;
return new class extends WPH_Step {
public function id(): string { return 'database'; }
public function title(): string { return 'Step 9 — Database'; }
public function sub_items(): array {
return [
'Run a database optimisation (via WP-CLI: wp db optimize, or via a plugin such as WP-Optimize)',
'Check for and remove any spam comments if comment moderation hasn\'t been keeping up',
'Check post revisions — if excessive (thousands), note for client discussion on whether a revision limit should be set',
];
}
};