Implement more granular backend validation for GrowthBook experiments
What?
- New validation rules in
util/experimentValidationRules.js
validateRiskLevel()blocks empty risk level and 'Risk assessment pending' (mirrors TKUI)validateRiskLevelForAttributeschecks for contextual attributes per https://wikitech.wikimedia.org/wiki/Test_Kitchen/Contextual_attributes#Privacy_considerationsvalidateSecurityLegalReview()requires a review link when risk isTier 1: High riskorTier 2: Medium risk.
- Updated GB validator in
validation/growthBookExperimentValidator.js
validateExperiment()now readscustomFields.risk_level/customFields.security_review_url.validateAllExperiments()now returns { validated, invalid } (the invalid list carries { id, trackingKey, errors }) so the caller can act on rejects.
- Update invalid experiments as "draft" in
service/growthBookClient.js,experimentConfigurationService.js
- New
updateExperimentStatus(id, 'draft')is aPOST {api_url}/{id}. process()marks each invalid experiment as "draft" back in GrowthBook but only when saved groups were available the poll cycle. Otherwise wiki targeting can't be resolved and an experiment could fail (and be wrongly updated as "draft"). This reuses the existing preservation check. Per-experiment failures are logged + counted, never aborting the poll.- Added a
growthbook_experiment_drafts_total{outcome}metric.
- Introduce
experimentUsesSavedGroupsintargetingConditionParser.js(true when the current phase'ssavedGroupTargetingreferences a saved group).
- When saved groups fetch is N/A, the code used a single
savedGroupsAvailableflag to skip marking all invalid experiments as draft and preserve all GB experiments. But saved groups availability only affects one validation rule (wiki targeting), so experiments failing for unrelated reasons were wrongly protected from eviction from the cache. - Validator tags each invalid experiment with
usesSavedGroupsso when saved groups are N/A,process()still marks invalid experiments as draft but filters out only the saved-group-dependent experiments - `preservedExperiments keeps only these instead of all GB experiments
- Documented in the
process()doc comment thatsavedGroupsAvailablereflects fetch success, not existence. With no saved groups configured (current state), the fetch resolves empty, the logic remains dormant, and the poll cycle matches the no-saved-groups baseline. It works automatically once saved groups are created in GrowthBook.
- Add/update relevant tests
Why?
Since custom hooks are not available yet in GrowthBook UI for experiments, we need to do custom validation in the TK backend during GrowthBook-sourced experiments ingestion in the poll cycle.
Notes:
- This MR excludes validation for OKRs and Teams -
will be done in a follow up MR. Implement in the future when needed per standup 6/15/26 - Already covered by existing code (no changes needed):
- Tracking key uniqueness, GB precedence
- Stream name validation via Action API
- Traffic per wiki rules