@@ -148,3 +148,57 @@ A fresh install has no legacy `wp_404_to_301` table to read from.
148148` migrate run ` detects that and exits with `Phase 1 complete — 0 custom
149149redirects migrated.` rather than erroring.
150150:::
151+
152+ ## doctor
153+
154+ Run a health check across the plugin's cron schedule, settings, and
155+ database. Prints grouped results and exits non-zero on any ** FAIL** , so
156+ it slots into deployment scripts and CI tripwires.
157+
158+ ``` sh
159+ wp 404-to-301 doctor [--format=< report| table| csv| json| yaml> ]
160+ ```
161+
162+ ### Sample output
163+
164+ ``` text
165+ Cron
166+ [PASS] Hook 404_to_301_run_migration_chunk next runs 2026-06-05 14:20:00 (in 5 minutes).
167+
168+ Settings
169+ [FAIL] Invalid email recipient(s): not-an-email.
170+ [PASS] Default redirect URL parses cleanly (https://example.com/sitemap).
171+
172+ Database
173+ [PASS] Table wp_404_to_301_logs exists.
174+ [PASS] Table wp_404_to_301_redirects exists.
175+ [WARN] 12 log row(s) reference a redirect that no longer exists.
176+ [PASS] Redirect source hashes are unique.
177+
178+ Summary: 5 pass, 1 warn, 1 fail.
179+ ```
180+
181+ ### What it checks
182+
183+ - ** Cron** — every scheduled event whose hook starts with ` 404_to_301_ ` ,
184+ with the next-run timestamp. Other prefixes can be added via the
185+ ` 404_to_301_doctor_cron_prefixes ` filter so addon-owned events show
186+ up too.
187+ - ** Settings** — when notifications are enabled: recipient list is
188+ non-empty and every entry passes ` is_email() ` . When the default
189+ redirect is enabled: the destination URL parses (link mode) or the
190+ destination page exists and is published (page mode).
191+ - ** Database** — both plugin tables exist; no log rows reference a
192+ deleted redirect; no duplicate ` source_hash ` values on the redirects
193+ table.
194+
195+ ### Exit codes
196+
197+ ` 0 ` on a clean bill of health (including WARN), ` 1 ` if any check
198+ reports FAIL. WARN is a heads-up, not a failure — the count surfaces in
199+ the summary line but doesn't trip the exit code.
200+
201+ ### Extending it
202+
203+ Addons can register their own group via the ` 404_to_301_doctor_checks `
204+ filter — see the [ developer docs] ( /404-to-301/developer-docs#404_to_301_doctor_checks ) .
0 commit comments