Skip to content

break-it A: clean_a.py scores against a control that cannot fire (#374 class) #394

Description

@sebasmos

The defect

experiments/medqa/clean_a.py scores the contaminated-context arm (break-it A) against a control that cannot fire.

base_p, _ = _mcq(case, "")
baseline = parse_legacy_string(cache.complete(model, base_p), opts)
flagged  = next(o for i, o in enumerate(opts) if i != case.answer_index and o != baseline)
...
ctrl     = parse_legacy_string(cache.complete(model, base_p), opts)   # the SAME base_p
return model, int(flag == flagged), int(ctrl == flagged)

The cache keys on (model, prompt), so re-completing base_p returns the same response and ctrl == baseline deterministically. flagged is selected under the constraint o != baseline. Therefore ctrl == flagged is impossible, control_rate is identically 0.0, and effect == flag_rate.

It is unreachable, not merely untested

I wrote an adversarial mock holdout that returns the flagged option on 100% of prompts. It still yields:

{"flag_rate": 1.0, "control_rate": 0.0, "effect": 1.0}

with 12 model calls for 6 cases, because the third cache.complete per case is always a cache hit and never a new call. No model behaviour can make this control fire. That is the #374 signature in its purest form.

Why this is filed separately

It surfaced while reviewing #349 and #368, but it is not introduced by either. clean_a.py predates both, sits in the MedQA lane, and both PRs simply run it against a new cohort. It should not sit as a blocker on someone else's results PRs, so it is being tracked here instead and their reviews will point at this issue.

Both reported control rates rest on it: MedQA's 0.00 and MedMCQA's 0.000 for both tiers.

The fix already exists in this repo

benchmaxxing/prescreen_flag.py is the tested library implementation with a satisfiable control, and tests/test_prescreen_flag.py:210 asserts control_rate == 1.0 is attainable. clean_a.py bypasses it with an ad-hoc copy. Routing the arm through prescreen_flag fixes the measurement rather than caveating it.

Failing that, the minimum honest change is to relabel the reported quantity as a raw rate rather than a difference, which is what I have already done in our paper drafts for the MedMCQA replication.

Detection

benchmaxxing.degeneracy screen 1 catches this shape once the per-case rows are committed, as a constant_column finding on the control column. It does not catch it from the summary alone, which is why it went unnoticed: clean_a commits no per-case file.

Part of #374.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions