experiments: server-side bucket selector + UpgradeButton variants (P1 of pricing experiments)#41
Merged
Merged
Conversation
… of pricing experiments)
Adds an internal/experiments package with a deterministic
SHA256(identifier+salt) mod len(variants) selector and a registry of
active experiments. The first registered experiment is UpgradeButton
with variants {control, urgent, value}.
The variant assignment is exposed on GET /auth/me as a new
`experiments` map keyed by experiment name, so the dashboard learns
the user's bucket for every active experiment in one round-trip.
POST /api/v1/experiments/converted records a conversion in the audit
log with kind = "experiment.conversion" and metadata = {experiment,
variant, action_taken}. The handler verifies the variant matches the
server's bucket for the caller (rejects stale clients) before
writing.
Bucketing identifier:
- /auth/me — team_id (caller is always authenticated here)
Tests:
- internal/experiments: 7 tests (determinism, distribution ~33/33/33
across 1000 ids, salt isolation, valid-variant invariant)
- internal/handlers: 6 tests (/auth/me embeds the field, POST writes
audit_log, rejects unknown experiment / invalid variant / variant
mismatch / no-auth)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4 tasks
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.
Summary
internal/experiments— deterministic SHA256(identifier+salt) mod N variant selector with a compile-time registry. First experiment registered:upgrade_buttonwith variants{control, urgent, value}.GET /auth/menow returns anexperimentsmap (e.g.{"upgrade_button": "urgent"}) so the dashboard learns every active bucket in one round-trip. Identifier isteam_id.POST /api/v1/experiments/convertedwrites anaudit_logrow (kind = experiment.conversion, metadata ={experiment, variant, action_taken}). Server-side guard rejects unknown experiments, invalid variants, and variants that don't match the server's own bucket for the caller.Test plan
go test ./internal/experiments/...— 7 tests pass (determinism, ~33/33/33 distribution across 1000 ids, salt isolation, valid-variant invariant)go test ./internal/handlers/ -run 'TestExperiments|TestGetCurrentUser_IncludesExperiments'— 6 tests pass against the real test-pgmake test-unit— all packages green except a pre-existingplans/TestAll_ReturnsAllPlansfailure (master/HEAD also fails this; verified viagit stash)experiments.upgrade_buttonfrom/auth/meand fires the conversion endpoint on click (separate PR,dashboard pricing/p1-ab-button-fresh)Audit row written (sample)
Follow-ups (intentionally not in this PR)
experimentsfield on/auth/me(the spec file is 1800 lines and the dashboard doesn't read it)./auth/meis auth-only so there's no fingerprint fallback path here. Add when an experiment needs to target the anon funnel.🤖 Generated with Claude Code