Performance: add a My Jetpack scenario (timing + bundle-size) to CodeVitals tracking - #50322
Conversation
|
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! |
6b34276 to
8c94a3d
Compare
The fixture site URL (http://localhost:<port>) has no dot, so Status::is_local_site() treats it as local and Jetpack enters offline mode, which makes My Jetpack's Initializer::should_initialize() return false — the page never registers. This fixture simulates a production, connected site, so force offline mode off. Install-wide: one WordPress install serves every scenario, so this shifts what the existing Dashboard and Forms trends measure by one step at the commit it lands (Jetpack runs more code paths when not offline). See the offline-mode attribution note in tools/performance/README.md.
Add the My Jetpack admin page (admin.php?page=my-jetpack) as a scenario, posting four production metrics (LCP, TTFB, FCP, decodedBytesKB). My Jetpack is the heaviest Jetpack admin bundle, so it is where a bundle-size regression is most damaging. Reuses the jetpack-connected WordPress instance (page- targeted scenario), modeled on formsResponses. - scenarios.js: new myJetpack scenario; four production keys under the same owner waiver as the Dashboard/Forms keys; minResourceCount floor 64 (~70% of the observed ~92-resource load) guards a truncated capture. - post-to-codevitals.test.js: pin the scenario's keys/types and navigation contract, mirroring the formsResponses config test. - README.md: metrics table, offline-mode attribution note with measured before/after deltas, known fixture behavior, and the #50291 dependency.
Address review feedback on the My Jetpack CodeVitals scenario: - Pin minResourceCount to its exact value (64) and exercise assertCaptureComplete at the 63/64 boundary, instead of the loose >=40 && <90 range a margin-eroding edit could pass silently. - Add an 11-key dry-run that drives a myJetpack summary through the poster, proving the four my-jetpack-connection-sim-* keys reach the payload with the right values (parity with the Forms dry-run). - Correct the scenarios.js comment: the .jp-admin-page frame renders with its children in one commit, so networkidle (not the frame selector) is the load-bearing capture-completeness gate.
Round-2 review accuracy fixes (no behavior change): - expectUrlIncludes test comment: it does not catch the onboarding redirect (My Jetpack is a single-slug SPA, so every view keeps page=my-jetpack). Credit the .jp-admin-page selector as the real wrong-view guard and scope expectUrlIncludes to off-page redirects. - Hydrate/networkidle comment: neither the .jp-admin-page frame nor networkidle alone guarantees the async card tail; state that completeness rests on networkidle plus the stable resource count, with the minResourceCount floor as the gross-truncation backstop. - myJetpackSummary JSDoc: only LCP/decodedBytesKB track observed medians; label ttfb/fcp as in-range fillers. - Pin the assertCaptureComplete 63-resource throw message, matching the sibling boundary tests.
My Jetpack joined Forms as a second consumer of the shared SANITY_RANGES.decodedBytesKB row; the comment named only Forms, which could mislead a future editor into tightening the bounds to one page's profile and clipping the other. Documentation only.
- simulate-wpcom-connection: Status::is_offline_mode() falls back to the stored jetpack_offline_mode option when the filtered value is false, so the __return_false filter alone can be overridden by a dirty/reused fixture DB with the option set to 1. Add pre_option_jetpack_offline_mode => __return_zero so the option read can't silently re-enable offline mode and break every scenario's boot. - README: the bundle-size metric line still said it is posted 'only for formsResponses', which this PR's own My Jetpack section below contradicts. Note both wp-build dashboards post it, not the Dashboard.
…ync) #50291 merged to the monorepo on 2026-07-08; the pinned jetpack-production mirror the fixture clones does not yet carry it. Reword the README from an unmerged-PR blocker to a merged-but-unpropagated prerequisite, matching the verified mirror state.
8c94a3d to
c6a94d8
Compare
| // offline by default): the simulate-wpcom-connection mu-plugin flips it. See the README | ||
| // offline-mode attribution note. | ||
| path: '/wp-admin/admin.php?page=my-jetpack', | ||
| waitForSelector: '#my-jetpack-container .jp-admin-page', |
There was a problem hiding this comment.
One thing worth resolving before merge — and thank you for documenting the mirror dependency so clearly, it's what made this easy to spot.
The failure here isn't graceful. When this waitForSelector times out (which the README says is the expected state until the mirror carries #50291), measure-lcp.js still ends with process.exit( hasFailures ? 1 : 0 ), and run-performance-tests.js runs it via execFileSync — so the non-zero exit throws and the runner process.exit(1)s before the CodeVitals posting step. Net: Dashboard and Forms get measured fine but never posted — every trend gets a hole for that build, not just My Jetpack. (--allow-codevitals-failure can't rescue it either; it only guards the post step, which is never reached.)
Since HEAD is still "awaiting mirror sync," merging before the mirror updates would blank out posting for all three trends. Two ways out:
- (a) Hold the merge until the mirror carries My Jetpack: register wp-build-polyfills so the app (and Boost) loads without Gutenberg on WP < 7.0 #50291 (the stated plan) — totally fine, but nothing enforces that gate in code, so it'd help to flag it explicitly for whoever merges.
- (b) Make degradation graceful: don't let a non-baseline scenario's measurement failure block posting of the scenarios that did measure. That removes the landmine permanently rather than just for this PR.
The per-scenario try/catch already signals the intent to isolate failures — it's just the terminal process.exit(1) + the sync short-circuit in the runner that undo it for the posting phase.
There was a problem hiding this comment.
Thanks for the thorough trace! You've read the mechanics exactly right. One mitigating detail: the poster already skips an errored scenario and posts the rest (post-to-codevitals.js), so the runner's short-circuit is the only gap.
Good news on both fronts since your review:
-
The gate condition now holds.
jetpack-productiontrunk picked up My Jetpack: register wp-build-polyfills so the app (and Boost) loads without Gutenberg on WP < 7.0 #50291. I reset my checkout to a cleanorigin/trunkand re-ran the full pipeline: My Jetpack renders and measures (median LCP 684 ms, 5/5 iterations, all four metrics in range). Forms still hits its pre-existing local hang (/wp/v2/settingsstalls past 60 s; unrelated to this PR, measures fine in CI), and the dry-run poster skipped it with a warning while keeping the other keys. I've updated the README and PR description to match. -
On (b): agreed, and I'd like to do it as a follow-up. If the runner posts survivors and still exits non-zero, a retried red build re-appends them as duplicate trend points (CodeVitals is append-only, dedup off by default). If it exits zero, a scenario failure goes silent. That trade-off deserves its own PR, likely a per-scenario
optionalflag. I've filed it as FORMS-728 and will pick it up right after this lands.
| add_filter( 'jetpack_offline_mode', '__return_false' ); | ||
| add_filter( 'pre_option_jetpack_offline_mode', '__return_zero' ); |
There was a problem hiding this comment.
Just a reminder rather than a change request: since this flip is install-wide, please make sure the CodeVitals annotation lands on the existing wp-admin-dashboard-* / forms-responses-* keys at the landing commit — the +9 resources / +107 KB step will read as a regression later without it. Easy to drop since it's a non-code action.
Also wanted to call out the nice detail here: using __return_zero (not __return_false) for the pre_option_ filter is exactly right — pre_option only short-circuits get_option when the value is !== false, so __return_false would quietly fall through to the stored option. The inline comment explaining that is much appreciated. 🙏
There was a problem hiding this comment.
Noted, and it won't get dropped: annotating the existing wp-admin-dashboard-* / forms-responses-* keys at the landing commit is on the landing checklist, and the PR description calls it out in the rollout notes for the same reason.
Thanks for the kind words on __return_zero! The pre_option short-circuit was subtle enough to bite silently, so the comment felt worth the lines.
jetpack-production trunk now carries #50291's WP_Build_Polyfills registration; a clean checkout of mirror commit 9ef44a8 renders My Jetpack and passes every capture guard (verified 2026-07-10, full local run). Reword the README so the polyfill reads as a baseline prerequisite instead of a pending blocker, and note that the pre-existing local Forms /wp/v2/settings hang persists on the current mirror.
…hift The attribution note said to expect 'a one-commit step ... not a level shift', which reads as if later points return to the old baseline. The offline-mode filters are unconditional, so every measurement after the landing commit runs the non-offline fixture: the Dashboard/Forms trends settle at the new level permanently. Say that, so graph readers treat it as a measurement-boundary change rather than an ongoing regression.
There was a problem hiding this comment.
Pull request overview
Adds My Jetpack page-load performance tracking into the existing tools/performance CodeVitals pipeline so regressions in load timing and overall runtime payload size are visible as trend changes (FORMS-717), reusing the existing simulated-connection WordPress fixture.
Changes:
- Introduces a new
myJetpackscenario targetingwp-admin/admin.php?page=my-jetpack, capturing LCP/TTFB/FCP plusdecodedBytesKBto new production CodeVitals keys. - Updates shared safeguards/docs to reflect the new scenario and the broader applicability of the
decodedBytesKBsanity range. - Forces Jetpack offline mode off in the simulated-connection mu-plugin so My Jetpack initializes in the localhost-based fixture; adds unit coverage to pin scenario config + posting payload.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| tools/performance/scripts/scenarios.js | Adds myJetpack scenario configuration and updates decodedBytesKB sanity-range commentary to cover both Forms and My Jetpack. |
| tools/performance/scripts/post-to-codevitals.test.js | Extends unit tests to cover the new scenario’s metrics wiring, capture guards, and dry-run payload accumulation. |
| tools/performance/README.md | Documents the new scenario, its prerequisites, and updates safeguard text to include the production-key waiver for myJetpack. |
| tools/performance/docker/mu-plugins/simulate-wpcom-connection.php | Disables Jetpack offline mode (with option fallback defense) to ensure My Jetpack renders/initializes in the fixture. |
Fixes FORMS-717
Why: My Jetpack ships the heaviest Jetpack admin bundle, but nothing tracks how long it takes to load or how big it is. This adds a My Jetpack scenario to the
tools/performanceCodeVitals pipeline so a load-time or bundle-size regression shows up as a trend break instead of a user complaint.Proposed changes
myJetpackscenario that measuresadmin.php?page=my-jetpackand posts LCP, TTFB, FCP, and bundle size (decodedBytesKB) to four new production keys (my-jetpack-connection-sim-*).jetpack-connectedDocker fixture as a page-targeted scenario, the same wayformsResponsesdoes, so no new service is added.localhost(no dot), which Jetpack reads as a local site and skips init. Two filters are needed becauseStatus::is_offline_mode()falls back to the stored option:jetpack_offline_mode => __return_falseandpre_option_jetpack_offline_mode => __return_zero(guards against a reused/dirty fixture DB).Slice 1 of FORMS-717. Settings, Stats, frontend, and control scenarios stay out of scope.
One caveat: that offline flip runs install-wide, so it nudges the existing Dashboard and Forms trends by one small step at the landing commit (+9 resources, +107 KB decoded; timing stays within noise). Those keys should get a CodeVitals annotation at that commit so the step is not misread as a regression.
Related product discussion/links
Does this pull request change what data or activity we track or use?
Yes, narrowly: it adds four internal performance-telemetry keys (
my-jetpack-connection-sim-*: LCP, TTFB, FCP,decodedBytesKB) to CodeVitals. These are aggregate page-load numbers captured from a synthetic Docker fixture, extending the existing Dashboard/Forms perf trends. No real users, no PII, no cookies, no end-user tracking. No Privacy Updates label needed.Testing instructions
All commands run from
tools/performance/.One-time setup:
1. Unit tests (no Docker needed, ~seconds):
pnpm test:unit # expect 110 passing2. Measure just the My Jetpack page locally, without publishing anything:
SCENARIO=my-jetpack pnpm test -- --skip-codevitals--skip-codevitalsruns the full Docker fixture and browser measurement but does not send results to the CodeVitals server, so no token is required and nothing is published. DropSCENARIO=my-jetpackto run every scenario.3. Inspect the payload a real post would send, without posting:
pnpm report:dry # exits 0, prints the four keys, no token neededResults from a local 5-iteration run against a clean mirror checkout (`9ef44a8`, 2026-07-10; medians, all in range)
minResourceCountis 64 (70% of the steady 92), so a truncated capture fails instead of posting an undercounted bundle size.Known, not caused by this PR: the
formsResponsesscenario cannot be measured in the local fixture because itsGET /wp/v2/settingsrequest hangs server-side past 60s (with offline on or off, so the flip is not the cause; re-verified 2026-07-10 on the current mirror). Flagged so a local Forms failure is not read as a regression from this change. Fixture realism is tracked in BOOST-456.