Skip to content

feat: support saved group references v2 - #143

Merged
andywhite37 merged 6 commits into
mainfrom
aw/saved-group-references-v2
Oct 2, 2026
Merged

andywhite37 merged 6 commits into
mainfrom
aw/saved-group-references-v2

Conversation

@andywhite37

@andywhite37 andywhite37 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

Features and Changes

Support v2 list/condition saved groups in both Python clients, with cycle detection and fail-closed legacy operators. Sync the v0.9.0 conformance corpus and expand the contributing guide.

Implementation prompt

Testing

  • All 1,196 tests pass locally; MyPy, Pyright, CI's blocking lint, and corpus freshness checks also passed. Covers sync/async evaluation, encrypted loading, refresh, concurrency, malformed conditions under $not, and boolean/number membership.
  • Benchmarks: three alternating runs per checkout on Python 3.13.15/macOS ARM64. Initial implementation (e6e7773 → 75c1c1a): existing evaluation workloads within ±4%, async throughput within 3%; v2 condition references added 0.17–0.26 µs versus inline. Final review fixes (f6e6101 → e964b5d): standard workloads changed −3.5% to +5.6%; an additional 1,000-entry numeric-list case matching [1, "absent"] added ~3.5–3.6 µs (52–55%) to distinguish booleans from numbers. Reusable benchmarks remain in tests/scripts/.

After merging

  1. Review and merge release PR #144 (3.2.0). Release Please created it after this PR merged. It updates CHANGELOG.md, growthbook/__init__.py, setup.cfg, and .release-please-manifest.json.
  2. Verify publication. Merging chore(main): release 3.2.0 #144 triggers the Release Please workflow, which creates the tag/GitHub release and runs the publish-pypi job to test, build, and upload the wheel and source distribution. No manual tag or upload is needed. Confirm the job succeeds and the package is available (substitute the actual version if the release PR changes):
    python -m pip download --no-deps --index-url https://pypi.org/simple 'growthbook==3.2.0' --dest /tmp/growthbook-release-check
  3. Open a separate PR in growthbook/growthbook. Add the published version at the top of packages/shared/src/sdk-versioning/sdk-versions/python.json:
    { "version": "3.2.0", "capabilities": ["savedGroupReferencesV2"] }
    Earlier capabilities are inherited. Do not add this capability to an older release. If registering before PyPI publication, use "prerelease": true and remove it after publication. Run pnpm --filter shared generate-sdk-report and include the generated packages/shared/src/sdk-versioning/CAPABILITIES.md. Merge and deploy that PR; handle this registry update explicitly rather than relying on the SDK's Update SDK Version dispatch workflow.
  4. Enable v2 on the SDK connection. After upgrading all applications using the connection to the published SDK version, update the connection's configured version and set Pass Saved Groups by reference → All Saved Groups (savedGroupFormat: "referencesV2"). Existing connections retain their previous saved-group format. Verify a condition-group rule evaluates correctly with the new payload.

@andywhite37
andywhite37 marked this pull request as ready for review September 28, 2026 21:54
@greptile-apps

greptile-apps Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

[Medium risk] Adds saved group references v2 evaluation support.

The PR appears safe to merge based on the reviewed changes and the current state of the previous threads.

Reviews (4) · Last reviewed commit: "fix: reject condition errors and disting..."

Comment thread growthbook/core.py Outdated
Comment thread growthbook/core.py Outdated
@andywhite37

Copy link
Copy Markdown
Contributor Author

@greptile review

@gazzdingo

gazzdingo commented Sep 30, 2026 •

Copy link
Copy Markdown

overall looks good just one thing that the robots flagged that we might want to change

Errors inside a saved group fail open under $not
_eval_saved_group catches TypeError/ValueError/etc. and returns False. A surrounding $not then flips that to True, so everyone matches. The same condition written inline raises instead.

groups = {"bad": {"type": "condition", "condition": {"$or": 5}}}
evalCondition({}, {"$not": {"$savedGroup": {"id": "bad"}}}, groups)  # True

Could we handle these the same way as RecursionError, at the top level, so the whole condition fails? A test for this case would be good too.

Smaller things, not blocking:

  • isIn treats True == 1, so a list group with [1] matches true. JS doesn't. This is older than this PR, but saved groups can now reach it.
  • The $savedGroups special case returns false for {"$savedGroups": null}, while JS returns true. Maybe drop it, or add the rule to JS and the test cases too.
  • The benchmark report might fit better in the PR description than in docs/.

@andywhite37

Copy link
Copy Markdown
Contributor Author

Moved evaluation errors to the outer boundary so $not cannot turn them into matches, and fixed boolean/number membership. Added sync/async tests; all 1,196 tests pass.

Kept $savedGroups rejection per the spec; JS needs a separate fix.

Removed the benchmark report from the repo. The draft PR description includes results and the ~3.5 µs numeric-array overhead.

@andywhite37

Copy link
Copy Markdown
Contributor Author

@greptile review

Comment thread growthbook/core.py
@andywhite37

Copy link
Copy Markdown
Contributor Author

@greptile review

@andywhite37
andywhite37 requested a review from gazzdingo October 1, 2026 16:49

@gazzdingo gazzdingo left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LGTM

@andywhite37
andywhite37 merged commit 6f07c49 into main Oct 2, 2026
8 checks passed
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.

2 participants