fix: Board admin submenu 404 — register after parent menu (v0.19.1)

Root cause: PRAutoBlogger_Board_Page::on_register_menu was hooked at
admin_menu default priority 10, same as PRAutoBlogger_Admin_Page::on_register_menu,
but registered first in register_admin_hooks(). WordPress calls
get_plugin_page_hookname() at add_submenu_page() time; if add_menu_page()
has not yet fired, $admin_page_hooks['prautoblogger-settings'] is unset and
WP falls back to the admin_page_prautoblogger-board hookname. At request
time WP recomputes the hookname as prautoblogger_page_prautoblogger-board,
finds no registered callback, and calls wp_die() — 404. Same mismatch
prevented board.css and board.js from loading (enqueue gate also checks
prautoblogger_page_ prefix).

Fix:
- class-prautoblogger.php: parent menu (Admin_Page) stays at priority 10;
  board submenu (Board_Page) moves to priority 11. Explicit priorities added
  with explanatory comment. See ARCHITECTURE.md §Board for the constraint.
- class-board-page.php: add $submenu reorder in on_register_menu() to keep
  Board as the first submenu item (primary click target), since the board
  entry can no longer be prepended by the auto-duplicate mechanism.
- tests/unit/Admin/BoardMenuRegistrationTest.php: 4 tests (new file):
  (1) priority relationship regression guard — FAILS on unfixed code (both
      at 10), PASSES with fix (board at 11); (2) fixed execution order;
      (3) broken execution order documentation; (4) parent slug correctness.
- ARCHITECTURE.md: §22b updated with menu-ordering constraint.
- CHANGELOG.md: v0.19.1 entry.

Deletion audit: only intentional removals (old wrong-order registration block).
class-prautoblogger.php is 407 lines (was 400 pre-fix, pre-existing P2 debt).

Agent-Session: board-404-hotfix-20260612
This commit is contained in:
peptiderepo
2026-06-12 08:29:00 +00:00
parent 49f17c3d6f
commit 6168520504
6 changed files with 347 additions and 10 deletions

View File

@@ -860,11 +860,11 @@ Tests use Brain\Monkey for WordPress function mocking (no database required). Th
### #22b: Kanban board as primary admin landing screen (v0.19.0)
### #22b: Kanban board as primary admin landing screen (v0.19.0 / v0.19.1)
The primary admin screen is now a kanban board (Direction C "Editorial Record") with four
columns: Generating | In Review | Published | Failed. A separate `class-board-page.php`
registers the `Board` submenu first so it appears before Settings in the nav.
registers the `Board` submenu and a `$submenu` reorder makes it the first (primary) entry.
`class-board-data-provider.php` orchestrates the four columns and handles WP_Query columns
(In Review, Published). Raw `prab_generation_log` queries (Generating, Failed) are in the
extracted `class-board-gen-log-query.php` (split for 300-line compliance) -- no new schema
@@ -875,6 +875,18 @@ fallback. M2 will rename Runs & Audit -> Articles and wire board cards to the Ar
Poll interval and published window are settings-backed, localized into board.js, never
hardcoded.
**Menu-ordering constraint (v0.19.1 hotfix):** `PRAutoBlogger_Board_Page::on_register_menu`
MUST be hooked at `admin_menu` priority 11 (or higher), AFTER
`PRAutoBlogger_Admin_Page::on_register_menu` which runs at priority 10. Reason: WordPress
computes the page hookname via `get_plugin_page_hookname()` at `add_submenu_page()` call
time. If `add_menu_page()` has not yet run, `$admin_page_hooks['prautoblogger-settings']`
is unset and WordPress falls back to the `admin_page_*` hookname. At HTTP request time WP
recomputes the hookname as `prautoblogger_page_prautoblogger-board` and finds no registered
callback -> `wp_die("Invalid plugin page")` 404. The enqueue-gate in `on_enqueue_assets()`
checks `prautoblogger_page_prautoblogger-board` and would similarly never fire with the
wrong hookname (board.css/board.js would silently not load). Regression test:
`tests/unit/Admin/BoardMenuRegistrationTest.php`.
## Cross-System LLM Budget Coordination
PRAutoBlogger and Peptide News both call OpenRouter and may share a single API key / billing account. Their combined spend should be considered when setting per-plugin budgets.