Skip to content

Extract A/B verbal-cluster splitting into a testable helper#1260

Merged
bnmnetp merged 1 commit into
mainfrom
extract-ab-clustering-helper
Jun 26, 2026
Merged

Extract A/B verbal-cluster splitting into a testable helper#1260
bnmnetp merged 1 commit into
mainfrom
extract-ab-clustering-helper

Conversation

@bnmnetp

@bnmnetp bnmnetp commented Jun 26, 2026

Copy link
Copy Markdown
Member

Summary

Follow-up to the ab-fixes merge. The A/B verbal-cluster building and
condition-balancing logic was inline inside the make_pairs request handler,
which made it impossible to unit test (it needs a DB, auth, and a live request).
This PR extracts that logic into a pure split_ab_conditions() function and
adds unit tests.

No behavior change in make_pairs — the inline block is replaced by a call
to the new helper.

What changed

  • split_ab_conditions(answerers, in_person_groups, rng=random) returns the
    (peeps_in_person, peeps_in_chat) partition. It contains the cluster-building
    (find_set_containing_string + grp -= clustered), greedy ~50/50 balancing,
    and the "seed each condition when possible" adjustment.
  • Randomness is injectable via the rng parameter so tests can pass a seeded
    random.Random for deterministic results.
  • make_pairs now calls the helper instead of inlining ~55 lines.

Tests

New test/bases/rsptx/assignment_server_api/test_make_pairs.py (9 tests,
pure/sync, no DB) covering:

  • singletons (no verbal partner) go to chat
  • a non-voting recorded partner stays in the same condition as the voter
  • recorded verbal groups are never split across conditions
  • overlapping recorded groups stay disjoint with no duplicates (the
    grp -= clustered guard)
  • both conditions are populated when possible
  • a lone multi-person cluster seeds the verbal side
  • greedy placement keeps the two conditions roughly balanced
  • determinism with a seeded rng

All 9 pass; black/flake8 clean on the changed files; assignment service
rebuild (uv run build -s assignment dev) passes including the full suite.

🤖 Generated with Claude Code

Pull the verbal-cluster building and condition-balancing logic out of
make_pairs into a pure split_ab_conditions() function and add unit tests.

The helper takes the answerers and recorded verbal groups and returns the
(in_person, chat) partition. Randomness is injectable via an rng parameter
so tests can pass a seeded random.Random for deterministic results. No
behavior change in make_pairs.

Tests cover: singletons go to chat, non-voting partners stay in their
partner's condition, verbal groups are never split, overlapping recorded
groups stay disjoint (the grp -= clustered guard), both conditions get
seeded when possible, balanced splitting, and determinism.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ALwXHQ2E7VCS2DN2spq1Y3
Copilot AI review requested due to automatic review settings June 26, 2026 13:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR extracts the A/B verbal-cluster construction and condition-balancing logic from the make_pairs request handler into a pure helper (split_ab_conditions) so it can be unit tested without DB/auth/request context.

Changes:

  • Added split_ab_conditions(answerers, in_person_groups, rng=...) to encapsulate clustering + balancing + “seed each condition when possible” behavior.
  • Updated make_pairs to call split_ab_conditions instead of inlining the logic.
  • Added a new unit test module covering key clustering, balancing, and determinism cases.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
bases/rsptx/assignment_server_api/routers/peer.py Introduces split_ab_conditions and replaces inlined A/B clustering/balancing logic in make_pairs with a helper call.
test/bases/rsptx/assignment_server_api/test_make_pairs.py Adds unit tests for the extracted helper, focusing on invariants, edge cases, and deterministic behavior with a seeded RNG.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +630 to +634
def split_ab_conditions(
answerers: list[str],
in_person_groups: list[set[str]],
rng: random.Random = random,
) -> tuple[list[str], list[str]]:
@bnmnetp

bnmnetp commented Jun 26, 2026

Copy link
Copy Markdown
Member Author

@sethbern - Any comments on this? See also #1261 which claude flagged as an edge case worth thinking about.

@sethbern

Copy link
Copy Markdown
Contributor

Looks good to me. I can take a look at at fixing #1261.

@bnmnetp bnmnetp merged commit 8983388 into main Jun 26, 2026
3 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.

3 participants