Skip to content

Content Gap Suggestions: shared Stats_Provider layer + dashboard widget (1/2 for #338) - #929

Open
zeus2611 wants to merge 12 commits into
WordPress:developfrom
zeus2611:feature/issue-338-content-gap-suggestions
Open

zeus2611 wants to merge 12 commits into
WordPress:developfrom
zeus2611:feature/issue-338-content-gap-suggestions

Conversation

@zeus2611

@zeus2611 zeus2611 commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

What?

See #338 (1 of 2 PRs — Traffic Surge Amplification follows separately)

Adds the Content Gap Suggestions experiment: a dashboard widget that surfaces new post topic ideas based on anonymized search patterns from a connected analytics plugin (Jetpack Stats for v1). Also adds the shared Stats_Provider abstraction and anonymization layer that PR 2 (Traffic Surge Amplification) will build on.

Why?

Issue #338 asks for two AI-assisted editorial signals sourced from site analytics: content gaps (this PR) and traffic-spike amplification (follow-up PR). The issue explicitly scoped v1 to Jetpack Stats only, a dashboard widget over an admin page, and no on-site search-log fallback — all reflected here. Any resulting draft must require human review before publishing, so suggestions only ever create a WordPress draft; nothing is published or posted automatically.

How?

  • includes/Stats/Stats_Provider.php — interface for analytics adapters (is_available(), get_search_queries(), get_post_traffic())
  • includes/Stats/Stats_Provider_Registry.php — resolves the first available provider; extensible via wpai_stats_providers filter
  • includes/Stats/Providers/Jetpack_Stats_Provider.php — wraps stats_get_from_restapi(). Note: Jetpack's search-terms data is external search-engine referrer terms, not on-site search queries — used here as a proxy signal for content gaps, not a literal zero-result-search report (Jetpack has no on-site search tracking)
  • includes/Stats/Anonymizer.php — the AI-safety boundary: drops low-count/single-occurrence terms, strips anything that looks like an email or long digit run (phone/order IDs), normalizes and merges the rest into aggregate patterns. Only these patterns are sent to the AI provider — raw query strings never leave this layer
  • includes/Experiments/Content_Gap_Suggestions/ + includes/Abilities/Content_Gap_Suggestions/ — the experiment and its ability, following the existing Abstract_Feature/Abstract_Ability pattern used elsewhere in the plugin. Structured JSON output via as_json_response()
  • includes/Admin/Dashboard/Content_Opportunities_Widget.php — dashboard widget, gated on the experiment being enabled. Renders only a mount point + "Generate" button; loading the dashboard never triggers an AI call, only clicking does
  • Frontend (src/experiments/content-gap-suggestions/): generate → list suggestions → "Create Draft" (creates a real post_status: draft post via REST and opens it in the editor) or "Dismiss"

Use of AI Tools

AI assistance: Implementation was planned with assistance from Claude Code (Anthropic). All code was reviewed, tested, and validated.

Testing Instructions

  1. npm install && composer install
  2. npm run build
  3. Enable Experiments globally, then enable "Content Gap Suggestions" (Settings → AI)
  4. Connect an AI provider (OpenAI or Google)
  5. Since Jetpack Stats won't have real on-site data on a fresh install, drop a throwaway mu-plugin registering a fake Stats_Provider via the wpai_stats_providers filter to get deterministic search-pattern data (see PR description or ask — happy to share the snippet)
  6. Dashboard → Content Opportunities widget → Generate suggestions
  7. Confirm a suggestion appears with title + outline
  8. Click Create Draft → confirm it opens in the editor with the title pre-filled and status is Draft (not published)
  9. Click Dismiss on another suggestion → confirm it's removed from the list

Screenshots or screencast

Before After
(no widget) Screenshot 2026-08-11 at 11 57 35 PM Screenshot 2026-08-11 at 11 56 52 PM Screenshot 2026-08-11 at 11 57 06 PM

Changelog Entry

Added - Content Gap Suggestions experiment: a dashboard widget that surfaces AI-generated post topic ideas from anonymized site search patterns (via Jetpack Stats), with a draft-only workflow requiring human review before publishing.

Open WordPress Playground Preview

- Shared Stats_Provider abstraction, Jetpack Stats adapter, and Anonymizer
- Content Gap Suggestions experiment, ability, and dashboard widget
- PHPUnit + e2e coverage; phpstan/phpcs clean
@codecov

codecov Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.90444% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.00%. Comparing base (775f152) to head (2657065).

Files with missing lines Patch % Lines
...ontent_Gap_Suggestions/Content_Gap_Suggestions.php 92.98% 8 Missing ⚠️
...ncludes/Stats/Providers/Jetpack_Stats_Provider.php 97.26% 2 Missing ⚠️
...s/Admin/Dashboard/Content_Opportunities_Widget.php 87.50% 1 Missing ⚠️
includes/Stats/Stats_Provider_Registry.php 93.75% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop     #929      +/-   ##
=============================================
+ Coverage      81.67%   82.00%   +0.33%     
- Complexity      3103     3186      +83     
=============================================
  Files            129      136       +7     
  Lines          12334    12627     +293     
=============================================
+ Hits           10074    10355     +281     
- Misses          2260     2272      +12     
Flag Coverage Δ
unit 82.00% <95.90%> (+0.33%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jeffpaul jeffpaul modified the milestones: 1.4.0, Future Release Aug 12, 2026
@zeus2611
zeus2611 marked this pull request as ready for review August 23, 2026 13:46
@zeus2611
zeus2611 requested a review from a team August 23, 2026 13:46
@github-actions

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: zeus2611 <n1schay@git.wordpress.org>
Co-authored-by: jeffpaul <jeffpaul@git.wordpress.org>

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

…8-content-gap-suggestions

# Conflicts:
#	tests/e2e-testing/e2e-testing.php
@zeus2611

Copy link
Copy Markdown
Contributor Author

@jeffpaul CI is passing. It's ready for review whenever you have the chance.

@jeffpaul jeffpaul modified the milestones: Future Release, 1.5.0 Sep 21, 2026
@jeffpaul
jeffpaul requested a review from dkotter September 29, 2026 13:44
…ions

- Drop removed enableExperiments helper from the e2e spec
- Dismiss the welcome guide reliably in the create-draft test
- Add Jetpack provider and ability tests with a Jetpack Stats stub
- Make ability registration test independent of test order
…ase labels

- Require manage_options in the ability and script enqueue to match the dashboard widget gate
- Rename 'Create Draft' to 'Create draft'
@zeus2611

zeus2611 commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

@dkotter CI is green and this is ready for review whenever you have the chance.

@jeffpaul jeffpaul left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not able to test this quickly (@zeus2611 if you can share the snippet in the PR description for others to try and load in test info), so this comment focuses more on the null/empty state...

Image

After clicking the Generate suggestions button the preceding copy and button are updated to just a Try again button with no other info.

Image

If there's an error or warning that we can throw in this case to give a site owner a little more context on what's not working, that would be ideal.

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.

New Experiments: Analytics-aware content and amplification recommendations

2 participants