AI Launchpad: complete tasks from wp-admin (completion audit fixes)#50005
Conversation
build_tasks() included any AI-selected task that existed in the catalog, ignoring the catalog's own is_visible_callback. Legacy Launchpad_Task_Lists::build() drops non-visible tasks (WooCommerce tasks with no WooCommerce, goal-gated payment tasks, etc.), so the AI path could surface tasks that can't be completed and whose CTA would 404. Filter the read path on is_visible() (made public on Launchpad_Task_Lists, mirroring the already-public load_calypso_path()). Gating the read rather than the write keeps the deterministic fallback usable, since its fixed per-goal lists also contain conditionally-visible tasks. Load wp-admin/includes/plugin.php first so visibility callbacks that call is_plugin_active() don't fatal in the REST context. Verified on Atomic: GET /wpcom/v2/ai-launchpad drops 17 inapplicable tasks (WooCommerce, Sensei, About-page, membership/subscriber) and returns 200. Part of DOTOBRD-480.
Six "acknowledgment" tasks (complete_profile, manage_subscribers, manage_paid_newsletter_plan, earn_money, start_building_your_audience, site_monitoring_page) have no completion signal when the launchpad runs in wp-admin: in Calypso they complete on click / page-visit, which writes the status to Calypso's selected site — never from the wp-admin context. The AI Launchpad runs in wp-admin (on both Simple and Atomic), so it has no way to tick them. Add a small allowlisted POST /ai-launchpad/complete-task endpoint that marks one of these tasks complete, restricted to the COMPLETE_ON_CLICK_TASK_IDS allowlist and to tasks actually on the site's AI-selected list. The tailored list calls it when the user clicks an acknowledgment task's CTA (awaited before the same-tab navigation, best-effort so a failed write never blocks the CTA). Verified in wp-admin on Atomic: clicking "Complete your profile" fires the POST (200) and the task ticks complete; the endpoint rejects non-allowlisted and unknown ids. Part of DOTOBRD-480.
connect_social_media, drive_traffic, and post_sharing_enabled have no add_listener_callback and complete in Calypso only, so a wp-admin launchpad never ticks them. Jetpack Social runs locally, though, so the real state is readable: a Publicize connection exists (connect_social_media / drive_traffic) or the Publicize module is active (post_sharing_enabled). Add AI_Launchpad_Social_Listener, which reconciles these on the AI Launchpad page load (admin_init, gated to that page and to incomplete AI-selected tasks so the Publicize lookup stays off every other admin page). There is no local "connection created" action on Atomic — connections are created through a proxied wpcom request — so a check-on-load is the available signal. Mirrors AI_Launchpad_Theme_Listener. Also raise the package's phpunit memory limit to 256M in the test bootstrap: the Brain Monkey / Patchwork suite instruments every loaded file and was already at the 128M default ceiling, so adding tests OOM'd mid-run. Test-only; production is unaffected. Verified on Atomic: with Publicize active and zero connections, the launchpad page load completes post_sharing_enabled and leaves the connection tasks incomplete; off-page loads are a no-op. Part of DOTOBRD-480.
add_about_page and update_about_page rely on the catalog's _wpcom_template_layout_category meta, which the dotcom editor toolkit provides (not registered on Atomic), and on the AI's createPatternPage only creating a draft — so they never tick in a wp-admin launchpad. Tag the AI-created About page with our own registered marker meta (_wpcom_ai_launchpad_about_page, set by createPatternPage) and add AI_Launchpad_About_Page_Listener, which watches that page's status transitions: first publish completes add_about_page, a later edit completes update_about_page. Independent of the layout-category meta. Verified on Atomic: a page created via /wp/v2/pages with the marker meta (201, meta set) completes add_about_page on publish and update_about_page on a subsequent edit. Part of DOTOBRD-480.
subscribers_added, import_subscribers, and add_10_email_subscribers complete in
Calypso only or via wpcom_launchpad_get_newsletter_subscriber_count, which
hard-requires IS_WPCOM and returns 0 on Atomic, so a wp-admin launchpad never
ticks them.
Add AI_Launchpad_Subscribers_Listener, which reconciles these on the AI Launchpad
page load (admin_init, gated to that page and to incomplete AI-selected tasks so
the lookup stays off every other admin page): subscribers_added / import_subscribers
complete once the site has at least one email subscriber, add_10_email_subscribers
once it has ten. The count comes from Jetpack's fetch_subscriber_counts()
(the Subscribe block's jetpack.fetchSubscriberCounts path, transient-cached) — the
blog-token GET /sites/{id}/subscribers/stats endpoint returns 400 missing_params
on Atomic, so the proven counts path is used instead. Mirrors
AI_Launchpad_Social_Listener.
add_10_email_subscribers also has an IS_WPCOM-only visibility gate
(wpcom_launchpad_are_newsletter_subscriber_counts_available), so the read-path
visibility filter would have hidden it on Atomic and made the completion
invisible. Add AI_Launchpad_REST::FORCE_VISIBLE_TASK_IDS, a documented allowlist
that skips the catalog visibility gate for tasks whose data the AI Launchpad
retrieves cross-platform.
Verified on Atomic: at the real count (0 email subscribers) nothing completes;
with a count of 10 all three tick; GET /wpcom/v2/ai-launchpad returns
add_10_email_subscribers despite its catalog visibility being false.
Part of DOTOBRD-480.
setup_ssh's real signal (an SSH user exists) is unreachable from the launchpad's Atomic context: the wpcom hosting/ssh-users endpoint rejects a blog-token request (401) and a Jetpack-user-token request (403, user_can_manage_hosting gate), and a8c_hosting_ssh_user_created fires wpcom-server-side, not on the WoA site. Calypso's own hosting form completes setup_ssh optimistically (sftp-form.tsx handleCreateUser calls completeTasks(['setup_ssh']) on SFTP-user create; it does not poll the endpoint). Reuse that strategy: add setup_ssh to the COMPLETE_ON_CLICK_TASK_IDS allowlist (server class-ai-launchpad-rest.php and client model.ts), so the launchpad ticks it on CTA click — the CTA opens the same hosting page where the user creates credentials. Relying on Calypso's own write instead is not viable for AI Launchpad sites: useCompleteLaunchpadTasksWithNotice filters requested slugs against the site's generic launchpad checklist and no-ops when a task isn't in it, and AI-only tasks are driven by the AI output option, not that checklist. Tracked as DOTOBRD-487. Verified on Atomic: GET /wpcom/v2/ai-launchpad renders setup_ssh, and POST /ai-launchpad/complete-task (200) ticks it complete. Part of DOTOBRD-480.
stripe_connected, set_up_payments, paid_offer_created, and newsletter_plan_created never complete in the AI Launchpad on Atomic: their catalog completion reads wpcom_launchpad_get_membership_settings(), which returns null under IS_ATOMIC, and stripe_connected / paid_offer_created recompute from it (ignoring any stored option) so an option-writing listener could not surface them either. The signals are readable locally on Atomic, though: Jetpack syncs the connected-account flag down as the jetpack-memberships-has-connected-account site option (wpcom memberships/connected-accounts.php pushes it to the Jetpack site; the launchpad write beside it targets the shadow blog and never reaches Atomic), and mirrors membership plans as the local jp_mem_plan CPT. Add AI_Launchpad_Memberships, which recomputes these four tasks' completion from Jetpack_Memberships' local signals (has_connected_account / has_configured_plans_jetpack_recurring_payments, plus the 'newsletter' variant), and have the REST read path (build_tasks) use it instead of the catalog callback for them. checklist_statuses in the response is overlaid to match. No cross-repo wpcom endpoint needed. Verified on Atomic: with no Stripe/plans all four report incomplete; with a synced connected-account option and jp_mem_plan posts (including a newsletter tier) all four report complete via GET /wpcom/v2/ai-launchpad. Part of DOTOBRD-480.
share_site has no real completion signal (sharing is a transient client action; even Calypso completes it optimistically when the user copies/shares the URL via its share-site modal) and no get_calypso_path, so the AI Launchpad rendered it as a card with no CTA — it could never be completed from wp-admin. Add a "Mark as complete" button shown for any complete-on-click task that has no CTA destination (isCompleteOnClickTask(id) && !canStart; currently just share_site). It POSTs the existing allowlisted complete-task endpoint and flips the card to done in place, only on a successful write. share_site is added to COMPLETE_ON_CLICK_TASK_IDS (server allowlist + model.ts). The acknowledgment tasks and setup_ssh are unaffected — they have destinations, so "Get started" still POSTs then navigates. Verified on Atomic: GET /wpcom/v2/ai-launchpad renders share_site with a null calypso_path (so the button shows), and POST /ai-launchpad/complete-task ticks it. Part of DOTOBRD-480.
The catalog sends connect_social_media to Calypso /marketing/connections even though it completes on the wp-admin Jetpack Social page, and the design "Select a design" tasks (design_completed, design_selected) to a Calypso setup step-flow rather than the theme browser — a poor fit for the wp-admin AI Launchpad. Add AI_Launchpad_REST::CTA_OVERRIDES, a read-side map applied in build_tasks that repoints connect_social_media to admin.php?page=jetpack-social (matching where it completes, and drive_traffic's CTA) and the design tasks to themes.php. Overriding on read leaves the shared catalog (used by the legacy launchpad too) untouched, mirroring FORCE_VISIBLE_TASK_IDS. admin_url() yields an absolute URL the client navigates as-is, so no client change is needed. Verified on Atomic: GET /wpcom/v2/ai-launchpad returns the wp-admin URLs for these tasks and leaves non-overridden tasks' paths unchanged. Part of DOTOBRD-481.
Render the full task catalog instead of the tailored list, so every task can be exercised from a single site without running the wizard. GET /ai-launchpad accepts all_tasks=1, which builds from the whole catalog with the per-site visibility gate bypassed (build_all_catalog_tasks; each task enriched in isolation so one that can't be built in this context is skipped). The client (isAllTasksMode) detects the param on the page URL, skips the wizard, and fetches with it. Read-only and admin-gated (rides on the existing edit_posts + eligibility check); the persisted tailored output is untouched. Verified on Atomic: GET ...?all_tasks=1 returns the full catalog (visibility bypassed, includes normally-hidden tasks), 200, with no enrichment failures; normal mode is unaffected. Part of DOTOBRD-480.
|
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.
Interested in more tips and information?
|
|
Thank you for your PR! When contributing to Jetpack, we have a few suggestions that can help us test and review your patch:
This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation 🤖 Follow this PR Review Process:
If you have questions about anything, reach out in #jetpack-developers for guidance! |
Code Coverage SummaryCoverage changed in 8 files. Only the first 5 are listed here.
4 files are newly checked for coverage.
Full summary · PHP report · JS report Coverage check overridden by
Covered by non-unit tests
|
- Suppress PhanUndeclared* on the Jetpack-plugin classes/functions the listeners reference (Publicize Connections/Publicize_Utils, fetch_subscriber_counts, Jetpack_Memberships) via @phan-file-suppress — they exist at runtime on Atomic and every call is guarded by class_exists/function_exists. - Cover AI_Launchpad_Subscribers_Listener::get_email_subscriber_count()'s real parse of fetch_subscriber_counts() (status/value handling) with a stubbed helper and a probe subclass. - Consolidate the ten AI Launchpad changelog entries into one. Part of DOTOBRD-480.
The subscriptions test fixture declares fetch_subscriber_counts() in its namespace, so phan (which analyzes test fixtures too) no longer sees the call as undeclared and the @phan-file-suppress became an UnusedPluginFileSuppression. Part of DOTOBRD-480.
connect_social_media, drive_traffic, and post_sharing_enabled point at the Jetpack Social admin page, which wpcom doesn't load on a private site (Publicize is gated the same way, Publicize_Setup::should_load()), so their CTA would 404. Hide them in build_tasks() when the site is private. The launchpad runs only on the wpcom platform, so the private-site flag (blog_public = -1, the core of Status::is_private_site()) is the whole condition; read it directly to avoid a hard Status-package dependency in this read path. Verified on Atomic: with blog_public = 1 the three Social tasks show; with -1 they are hidden and the rest of the list is unchanged. Part of DOTOBRD-481.
Proposed changes
Makes every AI Launchpad task completable from the wp-admin context (Simple and Atomic), where the legacy launchpad's Calypso-only completion mechanisms never fire. Spun out of the DOTOBRD-480 completion audit; also folds in the DOTOBRD-481 CTA fix.
By mechanism:
is_visible_callback—build_tasks()drops tasks the catalog would hide on this site, so the AI never surfaces inapplicable tasks whose CTA would 404.connect_social_media/drive_traffic/post_sharing_enabled) and the About-page pair (add_about_page/update_about_page).subscribers_added/import_subscribers/add_10_email_subscribers, via Jetpack'sfetch_subscriber_counts()); memberships (stripe_connected/set_up_payments/paid_offer_created/newsletter_plan_created, viaJetpack_Membershipslocal signals — no cross-repo endpoint needed).complete_profile,manage_subscribers, …), plussetup_ssh(its hosting endpoint rejects the Atomic context — blog-token 401 / user-token 403) andshare_site(a "Mark as complete" button, since it has no CTA destination).connect_social_media→ Jetpack Social and the design tasks → the theme browser instead of Calypso flows.?all_tasks=1testing param — renders the full task catalog (visibility bypassed) so every task can be exercised from a single site.Completion/CTA overrides live in the AI Launchpad (read-side or local listeners), so the shared launchpad catalog (used by the legacy launchpad too) is left untouched.
Related product discussion/links
Does this pull request change what data or activity we track or use?
No. The complete-on-click and "Mark as complete" actions reuse the existing
trackTaskClickedTracks event; no new data is collected.Testing instructions
The AI Launchpad is gated behind the
wpcom_ai_launchpad_enabledsite option (off by default) and a paid plan.&all_tasks=1to the page URL to render every task for testing.