Viewing files at commit 1c9b819ab86bCommit detailsView latest

Browse files
tests/test_workflow_publication_qualification.py 133 lines · 8.5 KB · python
1
import copy
2
from pathlib import Path
3
import sys
4
import unittest
5
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
6
from oakbench.public_output import workflow_evidence_failures, workflow_qualification_failures
7
from oakbench.populations import comparison_identity, comparison_conflicts
8
from oakbench.workflow_oracles import VERSION
9
10
11
def rows():
12
    return [dict(subject=subject, scenario='bugfix_test_loop', operation='workflow.total', run=run,
13
        bench_id='study', returncode=0, workflow_integrity_passed=True, oracle_version=VERSION,
14
        workflow_integrity_evidence_class='independent_committed_payload', workflow_integrity_source='git_committed_payload',
15
        git_head_instrument_sha256='a'*64, workflow_subject_binary_sha256='b'*64,
16
        env_isolation_version='3', randomized_subject_order=True, subject_order=['git','oak'], benchmark_track='agent-default')
17
        for subject in ('git','oak') for run in range(30)]
18
19
20
class WorkflowPublicationQualificationTest(unittest.TestCase):
21
    def test_qualified_observations_are_allowed(self):
22
        self.assertEqual(workflow_qualification_failures(rows()), [])
23
        self.assertEqual(workflow_evidence_failures(rows()), [])
24
25
    def test_thin_duplicate_missing_and_mixed_environment_block(self):
26
        for data in (rows()[:1], rows()+[rows()[0]], [{**row,'env_isolation_version': None} for row in rows()],
27
                     [{**row,'randomized_subject_order':False} for row in rows()],
28
                     [rows()[0], {**rows()[1], 'env_isolation_version':'2'}]):
29
            self.assertTrue(workflow_qualification_failures(data))
30
31
    def test_different_conditions_cannot_disappear_into_singletons(self):
32
        for changes in ({'benchmark_track':'core-equivalent'}, {'instruction_level':'zero-shot'},
33
                        {'agent':{'name':'x','model':'different'}},
34
                        {'workflow_integrity_evidence_class':'subject_produced_committed_payload'}):
35
            data=rows()
36
            for row in data:
37
                if row['subject']=='oak': row.update(changes)
38
            self.assertTrue(workflow_evidence_failures(data),changes)
39
40
    def test_comparison_identity_includes_correctness_class(self):
41
        left=rows()[0]
42
        right={**left,'workflow_integrity_evidence_class':'subject_produced_committed_payload'}
43
        self.assertIn('workflow_integrity_evidence_class',comparison_conflicts(comparison_identity(left),comparison_identity(right)))
44
45
    def test_failed_agent_trials_keep_failure_denominator(self):
46
        data=rows()
47
        for row in data:
48
            row.update(operation='agent.workflow', agent_launched=True,outcome_attribution='agent',success=True,outcome='pass')
49
        data[-1].update(workflow_integrity_passed=False,workflow_integrity_evidence_class='worktree_payload',
50
                        success=False,outcome='fail',returncode=1)
51
        self.assertEqual(workflow_evidence_failures(data),[])
52
        self.assertEqual(workflow_qualification_failures(data),[])
53
        data[-1]['outcome_attribution']='harness'
54
        self.assertTrue(workflow_evidence_failures(data))
55
56
class RealMixedOutcomePublicationTest(unittest.TestCase):
57
    def test_real_producers_preserve_failed_arm_in_publication_and_pair(self):
58
        import contextlib
59
        import io
60
        import json
61
        import os
62
        import shutil
63
        import subprocess
64
        import tempfile
65
        from unittest import mock
66
        import agent_workflow as aw
67
        import workflow_ab
68
        import publish_gate
69
        from oakbench.campaign_report import report_campaign
70
        if not os.environ.get('OAK_BENCH_OAK_READER_PYTHON'):
71
            self.skipTest('configure independent reader Python to exercise real decoder')
72
        with tempfile.TemporaryDirectory() as tmp:
73
            root=Path(tmp); fixture=root/'fixture'
74
            workflow_ab.make_bugfix_fixture(fixture)
75
            agent=aw.AgentConfig('mock','deterministic_mock','local',None,None,'1')
76
            original=aw.run_mock_agent
77
            def wrong_agent(subject, workflow, repo, *args, **kwargs):
78
                result=original(subject, workflow, repo, *args, **kwargs)
79
                (repo/'app/pricing.py').write_text('def apply_discount(cents, percent):\n    return cents\n')
80
                for command in ([str(subject.bin),'add','.'],[str(subject.bin),'commit','-m','incorrect answer']):
81
                    subprocess.run(command,cwd=repo,env=workflow_ab.base_env(),check=True,capture_output=True)
82
                return result
83
            observations=[]
84
            for kind in ('git','oak'):
85
                subject=workflow_ab.Subject(kind,kind,kind,Path(shutil.which(kind)))
86
                with mock.patch.object(aw,'run_mock_agent',side_effect=wrong_agent if kind=='git' else original):
87
                    row=aw.run_one(agent,subject,workflow_ab.WORKFLOWS['bugfix_test_loop'],fixture,0,
88
                        root/'work',root/'artifacts',60,'agent-default','minimal','full-docs','study','2026-09-18T00:00:00Z',
89
                        {'oak_hash':'test-binary-provenance','oak_status':'clean'},
90
                        {'randomized_subject_order':True,'subject_order':['git','oak']})
91
                observations.append(row)
92
            self.assertEqual([r['success'] for r in observations],[False,True])
93
            self.assertEqual(workflow_evidence_failures(observations),[])
94
            plan={'schema_version':1,'campaign_id':'study','plan_sha256':'bound','manifest':{},'schedule':[]}
95
            events=[]
96
            for index,row in enumerate(observations):
97
                trial_id=str(index); row['task_id']=trial_id
98
                plan['schedule'].append(dict(trial_id=trial_id,block_id='pair',ordinal=index,repetition=0,
99
                    workflow='bugfix_test_loop',instruction_level='full-docs',agent='mock',model=None,subject=row['subject']))
100
                events.extend([dict(event='started',trial_id=trial_id,plan_sha256='bound'),
101
                               dict(event='finished',trial_id=trial_id,plan_sha256='bound',outcome=row['outcome'],row=row)])
102
            report=report_campaign(plan,events,bootstrap_samples=20)
103
            comparison=report['groups'][0]['paired_comparisons'][0]
104
            self.assertEqual(comparison['complete_pairs'],1)
105
            self.assertEqual(comparison['success_delta_percentage_points'],100.0)
106
            # Repeated IDs below test the gate's mechanics only: not 30 measured trials.
107
            gate_rows=[{**row,'run':rep,'task_id':f'{rep}-{row["subject"]}'} for rep in range(30) for row in observations]
108
            path=root/'rows.jsonl';path.write_text('\n'.join(map(json.dumps,gate_rows)))
109
            output=io.StringIO()
110
            with contextlib.redirect_stdout(output): result=publish_gate.main([str(path)])
111
            self.assertEqual(result,0,output.getvalue())
112
            candidate=Path('/Applications/Xcode.app/Contents/Developer/usr/bin/git')
113
            instrument=Path('/Library/Developer/CommandLineTools/usr/bin/git')
114
            if candidate.exists() and instrument.exists():
115
                # Distinct installed vendor builds test configured identity plumbing,
116
                # not independent Git implementation or statistical power.
117
                with mock.patch.dict(os.environ, {'OAK_BENCH_GIT_INSTRUMENT': str(instrument)}):
118
                    passed_git=aw.run_one(agent,workflow_ab.Subject('git','git','Git candidate',candidate),
119
                        workflow_ab.WORKFLOWS['bugfix_test_loop'],fixture,0,root/'success-work',root/'success-artifacts',
120
                        60,'agent-default','minimal','full-docs','study','2026-09-18T00:00:00Z',
121
                        {'oak_hash':'test-binary-provenance','oak_status':'clean'},
122
                        {'randomized_subject_order':True,'subject_order':['git','oak']})
123
                self.assertTrue(passed_git['success'],passed_git.get('failure_reason'))
124
                self.assertEqual(passed_git['workflow_integrity_evidence_class'],'independent_committed_payload')
125
                self.assertNotEqual(passed_git['workflow_subject_binary_sha256'],passed_git['git_head_instrument_sha256'])
126
                successful_pair=[passed_git, observations[1]]
127
                self.assertEqual(workflow_evidence_failures(successful_pair),[])
128
                path.write_text('\n'.join(json.dumps({**row,'run':rep,'task_id':f'{rep}-{row["subject"]}'})
129
                                          for rep in range(30) for row in successful_pair))
130
                output=io.StringIO()
131
                with contextlib.redirect_stdout(output): result=publish_gate.main([str(path)])
132
                self.assertEqual(result,0,output.getvalue())
133