feat(myyoast): gradual rollout of the MyYoast OAuth connection#23376
Open
diedexx wants to merge 3 commits into
Open
feat(myyoast): gradual rollout of the MyYoast OAuth connection#23376diedexx wants to merge 3 commits into
diedexx wants to merge 3 commits into
Conversation
Adds Gradual_Rollout_Conditional, an abstract feature-flag conditional whose default state is a deterministic, slowly widening rollout across a share of sites. The YOAST_SEO_<FEATURE> constant stays an explicit override (defined true forces on, false forces off); when it is not defined, a stable hash of the feature name plus the site URL buckets the site into or out of the current rollout share (per-mille, 0-1000). Mixing the feature name into the hash avoids permanently "lucky" sites across features. MyYoast_Connection_Conditional extends it and ships at a 1% share. The machinery is disposable: at a 100% share the conditional reverts to extending Feature_Flag_Conditional directly and this class can be removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
When the MyYoast server engages its registration emergency brake it returns 503 temporarily_unavailable on Dynamic Client Registration. do_register() now detects this and throws a typed Registration_Temporarily_Unavailable_Exception (a Registration_Failed_Exception subtype, so existing handlers keep working) carrying the server's Retry-After hint. The hint is display-only: the WP-CLI register command shows when to try again, but nothing schedules or enforces a wait, and the HTTP client's backoff path is intentionally left untouched. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Introduces a deterministic, per-site gradual rollout mechanism for the MyYoast OAuth connection (instead of a global flip of YOAST_SEO_MYYOAST_CONNECTION), and adds a typed error path for the server-side “registration brake” (HTTP 503 temporarily_unavailable) including a display-only Retry-After hint surfaced via WP-CLI.
Changes:
- Added
Gradual_Rollout_Conditionalto enable feature flags for a stable per-mille cohort when noYOAST_SEO_<FEATURE>constant override is defined. - Switched
MyYoast_Connection_Conditionalto use gradual rollout (currently 1% / 10 per-mille). - Added
Registration_Temporarily_Unavailable_Exception, wired it into DCR registration handling, and added/updated unit tests (including WP-CLI messaging behavior).
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/conditionals/gradual-rollout-conditional.php |
New conditional implementing deterministic cohort rollout when no constant override is set. |
src/conditionals/myyoast-connection-conditional.php |
Moves MyYoast connection gating to the new gradual rollout conditional and sets rollout share. |
src/myyoast-client/infrastructure/registration/client-registration.php |
Detects 503 temporarily_unavailable, throws typed exception, parses Retry-After. |
src/myyoast-client/application/exceptions/registration-temporarily-unavailable-exception.php |
New exception type carrying optional retry delay. |
src/myyoast-client/user-interface/auth-command.php |
WP-CLI command surfaces “temporarily unavailable” with retry hint. |
tests/Unit/MyYoast_Client/Infrastructure/Registration/Client_Registration_Test.php |
Adds unit coverage for the new 503 brake behavior and Retry-After parsing. |
tests/Unit/Conditionals/Gradual_Rollout_Conditional_Test.php |
New unit tests for rollout cohort logic and constant override behavior. |
tests/Unit/Doubles/Gradual_Rollout_Conditional_Double.php |
Test double used to exercise the abstract rollout conditional. |
tests/Unit/Conditionals/MyYoast_Connection_Conditional_Test.php |
Updates expectation/documentation to reflect cohort fallback behavior. |
Coverage Report for CI Build 1Coverage decreased (-0.5%) to 53.23%Details
Uncovered Changes
Coverage RegressionsNo coverage regressions found. Coverage Stats💛 - Coveralls |
- Correct the MyYoast_Connection_Conditional docblock: the rollout ships at 1%, not 0, matching the returned share. - Simplify the boundary-test skip guard: the bucket is always 0-999 (% 1000), so the only clamp case to skip is a bucket of 0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Warning
This PR enabled the MyYoast connection feature flag for a small percentage of sites. Merge when the feature is ready or change the rollout share to
0.Context
The MyYoast OAuth/OIDC connection ships behind the
YOAST_SEO_MYYOAST_CONNECTIONfeature flag and will roll out to the 10M+ sites running Yoast SEO. Flipping it on globally would let every site attempt Dynamic Client Registration (DCR) against MyYoast on potentially the same day, with no production traffic shape to validate against. This PR makes the rollout gradual and adds the plugin side of the server's emergency brake, so we can enable the connection for a small, slowly widening share of sites and react before a problem reaches the full install base.This is the plugin half of the issue's recommended hybrid (Option D): a deterministic plugin-side cohort for the broad shape, plus the server-side 503 brake (already shipped in Yoast/Platform#4557) as the real-time panic button. The machinery is finite by design — once the connection reaches 100% with no regressions, the cohort conditional reverts to a plain feature-flag conditional and is removed in a follow-up cleanup.
Summary
This PR can be summarized in the following changelog entry:
Relevant technical choices:
Gradual_Rollout_Conditional(a new abstractFeature_Flag_Conditionalsubclass) treats a definedYOAST_SEO_<FEATURE>constant as an explicit override (trueforces on,falseforces off). When the constant is not defined, a stable hash of the feature name +site_url()(crc32 % 1000) buckets the site into or out of the current rollout share (per-mille, 0–1000).MyYoast_Connection_Conditionalextends it and ships at a 1% share.temporarily_unavailableis display-only.do_register()detects the server's registration brake and throws a typedRegistration_Temporarily_Unavailable_Exception(aRegistration_Failed_Exceptionsubtype, so existing handlers keep working) carrying theRetry-Afterhint. Nothing schedules or enforces a wait; the HTTP client's existing backoff path is intentionally left untouched.Gradual_Rollout_Conditionalcan and should be used for more similar features that we want to roll out to a smaller group first.Test instructions
Test instructions for the acceptance test before the PR gets merged
This PR can be acceptance tested by following these steps:
Override path: with
define( 'YOAST_SEO_MYYOAST_CONNECTION', true );inwp-config.php, the connection's WP-CLI commands (runwp yoast auth --helpto test) load (constant forces the feature on); withfalse, they do not.Cohort path: with the constant removed from
wp-config.php, the connection is enabled only for sites whosecrc32( 'MYYOAST_CONNECTION' . site_url() ) % 1000is below the 1% share (10). Most dev sites fall outside the cohort, so the connection stays inactive without the constant.Cohort path: edit
MyYoast_Connection_Conditional. change the10into1000. Verify that the MyYoast connection features are now available againRegistration brake: engage the server brake (per Yoast/Platform#4557) so DCR returns
503 temporarily_unavailable, then runwp yoast auth register. It reports "Registration is temporarily unavailable. Try again in N seconds." (or "Try again later." when noRetry-Afteris sent) instead of a generic failure. (coordinate with @diedexx or the Platform team).Relevant test scenarios
Multisite: the cohort hashes
site_url(), which is per-blog, so each subsite buckets independently (the intended behavior, matching the per-blog DCR registration model).Test instructions for QA when the code is in the RC
QA can test this PR by following these steps:
Impact check
This PR affects the following parts of the plugin, which may require extra testing:
MyYoast_Connection_Conditionalnow loads its integrations (key-rotation cron, cleanup, WP-CLI) for cohort sites with noYOAST_SEO_MYYOAST_CONNECTIONconstant set, where previously only an explicittrueenabled them. The cohort ships at 1%.do_register) — a new typed exception on the503path; the 201/other-error/transport-failure paths are unchanged.Other environments
[shopify-seo], added test instructions for Shopify and attached theShopifylabel to this PR.[yoast-doc-extension], added test instructions for Yoast SEO for Google Docs and attached theGoogle Docs Add-onlabel to this PR.Documentation
Quality assurance
grunt build:imagesand committed the results, if my PR introduces or edits images or SVGs.Innovation
innovationlabel.Fixes Yoast/reserved-tasks#1208