Problem
The 24-hour PHP error report contains a real fatal from the API plugin:
Cannot redeclare function extrachill_api_register_docs_info_routes() (previously declared in .../extrachill-api/inc/routes/docs/docs-info.php) in .../extrachill-api/inc/routes/docs/docs-info.php:15
Count: 1 in the last 24 hours.
Unlike the other current fatal signatures, this one is not from WP_CLI eval; it terminates while loading a plugin route file.
Code evidence
extrachill-api.php:73-90 recursively loads every PHP file below inc/routes/ with require_once $file->getRealPath().
inc/routes/docs/docs-info.php:10-15 registers a global callback and declares it unconditionally.
- A repository grep finds only this declaration and the recursive loader, so the second load likely comes from a duplicate plugin bootstrap or a path-identity/symlink mismatch that bypasses PHP's
require_once identity.
Investigation
- Capture request/runtime context for this signature and determine how the plugin or route file is loaded twice.
- Make plugin bootstrap idempotent at the owning boundary rather than adding scattered
function_exists() workarounds to route files.
- Audit whether other global route declarations share the same fatal risk.
Acceptance
- Reproducing a duplicate bootstrap does not redeclare route callbacks or fatal.
- Normal REST route registration still occurs exactly once.
- Add a focused regression test for duplicate bootstrap/path loading if the test surface supports it.
Constraints
Use the smallest owner-level fix. Do not introduce a new route registry or wrapper abstraction; WordPress hooks and the existing plugin singleton should remain the primitives.
Problem
The 24-hour PHP error report contains a real fatal from the API plugin:
Count: 1 in the last 24 hours.
Unlike the other current fatal signatures, this one is not from
WP_CLI eval; it terminates while loading a plugin route file.Code evidence
extrachill-api.php:73-90recursively loads every PHP file belowinc/routes/withrequire_once $file->getRealPath().inc/routes/docs/docs-info.php:10-15registers a global callback and declares it unconditionally.require_onceidentity.Investigation
function_exists()workarounds to route files.Acceptance
Constraints
Use the smallest owner-level fix. Do not introduce a new route registry or wrapper abstraction; WordPress hooks and the existing plugin singleton should remain the primitives.