Skip to content

Integrate Plugin Check Namer as standard AI Name Check - #1449

Open
davidperezgar wants to merge 12 commits into
trunkfrom
namer-integrated
Open

davidperezgar wants to merge 12 commits into
trunkfrom
namer-integrated

Conversation

@davidperezgar

@davidperezgar davidperezgar commented Aug 22, 2026

Copy link
Copy Markdown
Member

Summary

Closes #1370

Integrates the standalone Plugin Check Namer tool into Plugin Check as a new AI Name Check, available as a checkbox under the AI settings on the Tools > Plugin Check admin page, in the CLI (--ai-name), and via AJAX.

The check analyzes the plugin's display name (read from its own headers, no extra input required) and reports:

  • Disallowed / generic names (plugin_name_disallowed)
  • Possible naming issues (plugin_name_issue)
  • Possible trademark/ownership issues (plugin_name_trademark_issue)
  • Similarity with existing plugins in the directory (plugin_name_similarity)

Changes

  • Added WordPress\Plugin_Check\Checker\Checks\Plugin_Repo\AI_Name_Check, a new Static_Check registered in Default_Check_Repository under the ai_name slug (Plugin Repo category), reusing the existing AI_Check_Names / AI_Utils traits for the AI analysis logic.
  • Removed the standalone Namer_Page admin page and its dedicated plugin-check-namer.js script, since the functionality now lives inside the main Plugin Check tool.
  • Added Abstract_Check_Runner::set_use_ai_name() / should_use_ai_name() / get_ai_model_preference() so runners (AJAX, CLI) can toggle and expose the new option.
  • Added a --ai-name flag to the wp plugin check CLI command.
  • Added an "AI" section on the admin page with an "Analysis" and a "Name Check" checkbox (labels simplified, without a redundant "Enable" prefix).
  • Fixed a bug where AI Name Check results never appeared in the admin UI: Plugin_Request_Utility::get_runner() was always null during static-check-only AJAX requests (the runner is otherwise only registered via the object-cache.php drop-in used for runtime checks), so AI_Name_Check::run() silently bailed out even when the checkbox was checked. Added Plugin_Request_Utility::set_runner() and register the runner from Admin_AJAX::get_ajax_runner(), reusing that same instance in run_checks().
  • Added AI_Name_Check_Tests covering the new check.

Testing

  • Verified via WP-CLI: wp plugin check <plugin> --checks=ai_name --ai-name --require=./wp-content/plugins/plugin-check/cli.php.
  • Verified in the browser (Tools > Plugin Check admin page) that toggling "Name Check" on/off correctly shows/hides the AI-generated name findings, both with categories selected and with no category selected.
  • Confirmed no PHP fatal errors/notices in debug.log during either flow.

Disclaimer

Part of this PR (code and/or this description) was developed with the assistance of AI (Claude, via GitHub Copilot / Claude Code). All changes have been manually reviewed and tested by a human before submission.

Open WordPress Playground Preview

@github-actions

github-actions Bot commented Aug 22, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ishitaj34 <ishitaj34@git.wordpress.org>
Co-authored-by: davidperezgar <davidperez@git.wordpress.org>
Co-authored-by: ernilambar <nilambar@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

…ility

The new set_runner() method (added to support AI Name Check in AJAX-only
static check requests) pushed the public method count above PHPMD's
threshold. Suppressing the rule here follows the same pattern already used
in Admin_Page, Settings_Page, Abstract_Check_Runner and Check_Result.
@davidperezgar davidperezgar added this to the 2.2.0 milestone Aug 23, 2026
@ernilambar

Copy link
Copy Markdown
Member

Summary

Adds an ai_name static check that runs the existing Plugin Check Namer AI logic (AI_Check_Names trait) against the plugin's Name/AuthorName headers, plus a Plugin_Request_Utility::set_runner() hook so AJAX static-only requests can reach the runner. It does not implement the issue's actual ask — a dedicated "Check plugin name" checkbox under AI Analysis — and the PR description describes changes that are not in the diff.

✅ What's good

  • Reuses the existing AI_Check_Names / AI_Utils traits rather than duplicating the Namer prompt logic.
  • should_run_ai_check() correctly short-circuits on missing runner, AI disabled, and failed prerequisites/connectors, so zero cost when AI is off.
  • Diagnosis of the Plugin_Request_Utility::get_runner() null-on-static-AJAX bug is correct and real (initialize_runner() is only ever called from drop-ins/object-cache.copy.php:39).
  • Removing the duplicated runner-resolution block from run_checks() in favour of get_ajax_runner() is a genuine cleanup.
  • phpcs, phpstan, and phpmd all pass clean on the branch.

⚠️ Must-fix before merge

  • Scope: the issue's core requirement is missingIntegrate Plugin Check Namer into Plugin Check AI Analysis #1370 asks for a new checkbox ("Check plugin name") so the name check can be toggled independently. This PR adds no admin UI, no --ai-name CLI flag, and no set_use_ai_name()/should_use_ai_name(). It reuses should_use_ai(), so ai_name fires automatically for anyone who has the existing AI Analysis box ticked and runs the plugin_repo category — two unannounced extra LLM calls with no way to opt out except disabling all AI analysis.

  • PR description does not match the diff — it claims Namer_Page and assets/js/plugin-check-namer.js were removed, an "AI" admin section with an "Analysis"/"Name Check" checkbox was added, and a --ai-name flag was added. None of that is in the branch (git diff --stat trunk...pr-1449 touches 7 files; includes/Admin/Namer_Page.php and assets/js/plugin-check-namer.js are both still present). Either the code or the description needs to change before review can be meaningful.

  • includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php:200check_similar_plugins() is dead code. parse_analysis() returns only verdict, explanation, processed_data, token_usage (see AI_Check_Names::parse_prereview_response()), never a top-level confusion_existing_plugins. That key comes from the first (similar-name) query, whose structured payload is discarded in run_name_analysis() — only its text is folded into the prereview prompt. plugin_name_similarity can therefore never be emitted. (Namer_Page.php:172 has the same latent bug, which is presumably where this was copied from.)

  • includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php:189check_owner_issues() reads possible_owner_issues / owner_explanation, which are absent from get_prereview_schema() while additionalProperties is false. In the structured-output path plugin_name_trademark_issue can never fire either. Two of the four advertised result codes are unreachable.

  • XSS: raw AI output rendered unescapedtemplates/results-row.php:35 emits {{{data.message}}}. disallowed_explanation / naming_explanation are taken straight from processed_data (parse_prereview_response() only wp_kses_post()s the combined explanation string, not the individual fields the check consumes), as are the AI-supplied name values in the similarity list. The plugin name is attacker-controlled prompt input, so this is a prompt-injection → stored-HTML path into an admin screen. Escape with esc_html() (or at minimum wp_kses_post()) before passing to add_result_*_for_file().

  • includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php:203$plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] are accessed without isset(). Model output is untrusted; a partial object emits PHP 8 warnings mid-check. Guard the keys.

  • includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php:203sprintf( '%s (%s, %s active installs)', ... ) hardcodes user-facing English outside __(). Wrap it, or drop "active installs" from the format and build the string from translated parts.

  • CLI path still silently bails — the set_runner() fix is applied only in Admin_AJAX::get_ajax_runner(). Plugin_Check_Command.php:236-241 falls back to new CLI_Runner() without registering it, and initialize_runner() only runs from the object-cache drop-in. Without the drop-in installed, get_runner() is null and AI_Name_Check::run() returns immediately — the exact bug the PR says it fixed, in the exact invocation form listed under "Testing" (--require=./…/cli.php). Add Plugin_Request_Utility::set_runner( $runner ) there too.

  • includes/Checker/Abstract_Check_Runner.php:24@SuppressWarnings(PHPMD.TooManyFields) is unnecessary; the PR adds no new property, and phpmd passes on this file with the annotation removed (verified locally). Drop it.

  • @since x.x.x throughout — repo convention is a concrete version (e.g. @since 2.1.0 in AI_Provider_Check). There are no x.x.x or n.e.x.t placeholders anywhere in includes/ on trunk. Milestone is 2.2.0.

  • docs/checks.md — every registered check has a row (32 rows, incl. ai_provider at line 39); ai_name is not added. docs/ai-features.md also isn't updated.

💡 Suggestions (optional)

  • includes/Plugin_Main.php:12,84 — replacing the $namer_page_class string with a direct use/new Namer_Page() is unrelated to this issue and looks like leftover churn from the recover page commit (c68b1e6) that reverted the deletion. Revert it to keep the diff surgical.
  • includes/Checker/Abstract_Check_Runner.php:346 — widening should_use_ai() from protected to public just so a check can reach it, then guarding with method_exists() in the check, is the wrong seam. If checks are meant to read runner AI state, put should_use_ai() / get_ai_model_preference() on the Check_Runner interface and drop the method_exists() calls. Also inconsistent: get_ai_model_preference() is final public, should_use_ai() is not.
  • Architecture — every other check receives its configuration through Check_Result/Check_Context; this one reaches into a global static singleton. Consider threading the AI flag through the result/context instead, which would also remove the need for set_runner() entirely.
  • tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php — one test, covering only the bail path. It pokes the private static via reflection instead of using the set_runner() method this PR just added, and has no tearDown(), so a failure before line 29 leaks runner state into sibling tests. Add cases for the WP_Error path and each of the four result branches — those tests would have caught the two dead branches above.

Verdict

Request Changes — the issue's central requirement (a separate opt-in checkbox) is unimplemented, the PR description describes code that isn't in the diff, two of four result codes are unreachable dead branches, and raw AI output reaches an unescaped {{{ }}} template.


🤖 Review produced with Claude Code using Claude Opus 5 (1M context) (claude-opus-5).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integrate Plugin Check Namer into Plugin Check AI Analysis

3 participants