from pathlib import Path
import subprocess,sys,shutil
out=Path(__file__).resolve().parent;base=Path('/Users/yixiay/Documents/codexWorkSpace/issues/output/597084-review/impact-2026-11-04/source');root=out.parent/'code';old=out.parent/'review-round1-baseline';label='red' if '--baseline' in sys.argv else 'green'
java=out/'response-binding-src';shutil.copytree(out/'segment-wave-src',java,dirs_exist_ok=True);(java/'SegmentWaveProbe.java').unlink(missing_ok=True)
def stub(name,body):
 p=java/(name.replace('.','/')+'.java');p.parent.mkdir(parents=True,exist_ok=True);p.write_text('package '+name.rsplit('.',1)[0]+';\n'+body)
stub('cn.jojo.cc.biz.model.cache.FormAndDataCacheBo','@lombok.Data public class FormAndDataCacheBo{private Long businessId,segmentSourceFormDataId;private String outDataJson,segmentSourcePayloadHash,segmentSourceOutputHash;private com.alibaba.fastjson.JSON outDataJsonObj;}')
stub('cn.jojo.cc.biz.utils.UriToolsUtil','public class UriToolsUtil{public static String getPath(String s){return s;}}')
stub('cn.jojo.cc.common.dto.question.library.StandardQuestionDto','@lombok.Data public class StandardQuestionDto{private String questionKey;private cn.jojo.cc.common.dto.english.EnglishSegmentDto segments;}')
# Use the exact production RPC fill method body, not an imitation of its call wiring.
rpc=(root/'api/api-server/src/main/java/cn/jojo/cc/server/service/impl/QuestionServiceImpl.java') if label=='green' else (base/'api/api-server/src/main/java/cn/jojo/cc/server/service/impl/QuestionServiceImpl.java')
s=rpc.read_text();a=s.index('    private void fillSegments(');b=s.index('\n    }',a)+6
header=r'''import java.util.*;import java.util.stream.Collectors;import org.apache.commons.collections4.*;import org.apache.commons.lang3.*;
import cn.jojo.cc.biz.service.english.segment.*;import cn.jojo.cc.common.enums.*;import cn.jojo.cc.common.dto.english.*;import cn.jojo.cc.common.dto.question.library.*;import cn.jojo.cc.core.domain.english.*;import cn.jojo.cc.core.domain.question.library.*;import cn.jojo.cc.biz.model.cache.*;
public class ResponseBindingProbe {
 EnglishSegmentDeliveryBizService englishSegmentDeliveryBizService;
 static class Log{void error(String x,Object...y){}}Log log=new Log();
 void apply(List<StandardQuestionDto> questions,Map<String,SelectedSegmentOutput> selected){try{var m=Arrays.stream(getClass().getDeclaredMethods()).filter(x->x.getName().equals("fillSegments")).findFirst().get();m.setAccessible(true);if(m.getParameterCount()==2)m.invoke(this,questions,selected);else m.invoke(this,questions);}catch(Exception e){throw new RuntimeException(e);}}
 static void set(Object o,String n,Object v){try{var f=o.getClass().getDeclaredField(n);f.setAccessible(true);f.set(o,v);}catch(Exception e){throw new RuntimeException(e);}}
 static boolean has(Object o,String n){try{o.getClass().getDeclaredField(n);return true;}catch(Exception e){return false;}}
 static int failures;static void check(boolean b,String name){System.out.println((b?"PASS ":"FAIL ")+name);if(!b)failures++;}
'''
footer=r'''public static void main(String[]args){
 var v1=com.alibaba.fastjson.JSON.parseObject("{\"questionInfo\":{\"material\":\"v1\"},\"explicitNull\":null}");var v2=com.alibaba.fastjson.JSON.parseObject("{\"questionInfo\":{\"material\":\"v2\"},\"explicitNull\":null}");
 String h1=SegmentPayloadIdentity.hash(v1),h2=SegmentPayloadIdentity.hash(v2);
 check(!h1.equals(h2),"different generation input has different identity");
 check(h1.equals(SegmentPayloadIdentity.hash(com.alibaba.fastjson.JSON.parseObject("{\"explicitNull\":null,\"questionInfo\":{\"material\":\"v1\"}}"))),"canonical ignores object key order");
 check(!h1.equals(SegmentPayloadIdentity.hash(com.alibaba.fastjson.JSON.parseObject("{\"questionInfo\":{\"material\":\"v1\"}}"))),"canonical preserves explicit null versus absent");
 check(!SegmentPayloadIdentity.hash(com.alibaba.fastjson.JSON.parseObject("{\"a\":[1,2]}" )).equals(SegmentPayloadIdentity.hash(com.alibaba.fastjson.JSON.parseObject("{\"a\":[2,1]}"))),"canonical preserves array order");
 var cache=new FormAndDataCacheBo();cache.setBusinessId(8L);cache.setSegmentSourceFormDataId(7L);cache.setSegmentSourcePayloadHash(h1);cache.setOutDataJson("/v1.json");cache.setOutDataJsonObj(v1);cache.setSegmentSourceOutputHash(SegmentPayloadIdentity.outputHash(v1));
 SelectedSegmentOutput selectedV1=SelectedSegmentOutput.capture(DataBaseTypeEnum.TEACHING,8L,cache,"/v1.json");
 // Freeze v1 credentials first; then the DB and stored sequence advance to v2.
 cache.setSegmentSourcePayloadHash(h2);cache.setOutDataJson("/v2.json");cache.setOutDataJsonObj(v2);cache.setSegmentSourceOutputHash(SegmentPayloadIdentity.outputHash(v2));
 var row=new QuestionSegmentDo();row.setQuestionKey("q1");row.setQuestionId(1L);row.setContentType(1);row.setSegmentStatus(0);row.setSourceTextMd5(cn.jojo.cc.biz.utils.MD5Util.creatMd5("v2"));
 var stored=com.alibaba.fastjson.JSON.parseObject("{\"matches\":[{\"text\":\"v2\",\"type\":\"word\"}]}");stored.put("sourcePayloadHash",h2);stored.put("sourceFormDataId",7L);stored.put("sourceContentConfigId",8L);stored.put("sourceOutputPayloadHash",SegmentPayloadIdentity.outputHash(v2));row.setSegments(stored.toJSONString());
 var core=new cn.jojo.cc.core.service.english.QuestionSegmentCoreService();core.rows=List.of(row);
 var delivery=new EnglishSegmentDeliveryBizService();set(delivery,"questionSegmentCoreService",core);set(delivery,"englishDictConfig",new cn.jojo.cc.biz.common.config.EnglishDictConfig());set(delivery,"segmentMatchIdResolver",new SegmentMatchIdResolver());
 if(has(delivery,"questionCoreService")){var questions=new cn.jojo.cc.core.service.question.library.QuestionCoreService();var q=new QuestionDo();q.setId(1L);q.setQuestionKey("q1");questions.current=q;set(delivery,"questionCoreService",questions);var content=new SegmentContentSnapshotService();set(content,"segmentTextExtractor",new SegmentTextExtractor(){public Map<Integer,List<String>> extractRichTextsByContentType(QuestionDo x){return Map.of(1,List.of("v2"));}public String extractPlainText(String x){return x;}});set(delivery,"segmentContentSnapshotService",content);}
 var rpc=new ResponseBindingProbe();rpc.englishSegmentDeliveryBizService=delivery;var dto=new StandardQuestionDto();dto.setQuestionKey("q1");rpc.apply(List.of(dto),Map.of("q1",selectedV1));check(dto.getSegments()==null,"select v1 then DB/segments v2 then fill: no mismatched segments");
 var selectedV2=SelectedSegmentOutput.capture(DataBaseTypeEnum.TEACHING,8L,cache,"/v2.json");dto=new StandardQuestionDto();dto.setQuestionKey("q1");rpc.apply(List.of(dto),Map.of("q1",selectedV2));check(dto.getSegments()!=null,"same selected v2 and v2 sequence remains deliverable");
 cache.setSegmentSourcePayloadHash(null);var legacy=SelectedSegmentOutput.capture(DataBaseTypeEnum.TEACHING,8L,cache,"/v2.json");dto=new StandardQuestionDto();dto.setQuestionKey("q1");rpc.apply(List.of(dto),Map.of("q1",legacy));check(dto.getSegments()==null,"legacy cache without reliable metadata fails closed");
 check(SelectedSegmentOutput.capture(DataBaseTypeEnum.TEACHING,9L,cache,"/v2.json")==null,"content config mismatch rejected");
 if(failures>0)System.exit(1);
 }}'''
(java/'ResponseBindingProbe.java').write_text(header+s[a:b]+footer)
files=list(java.rglob('*.java'))
actual=['application/src/main/java/cn/jojo/cc/biz/model/bo/english/PhraseMatch.java','application/src/main/java/cn/jojo/cc/biz/model/bo/english/SegmentSnapshot.java','api/rpc-api/src/main/java/cn/jojo/cc/common/dto/english/EnglishSegmentDto.java','application/src/main/java/cn/jojo/cc/biz/service/english/segment/SegmentTextExtractor.java','application/src/main/java/cn/jojo/cc/biz/utils/MD5Util.java']
for p in actual:files.append(root/p if (root/p).exists() else base/p)
for n in ['SegmentPayloadIdentity','SelectedSegmentOutput','SegmentContentSnapshotService']:
 files.append(root/('application/src/main/java/cn/jojo/cc/biz/service/english/segment/'+n+'.java'))
p='application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentDeliveryBizService.java';files.append(old/p if label=='red' else root/p)
m2=Path.home()/'.m2/repository';names=['org/apache/commons/commons-lang3/3.9/commons-lang3-3.9.jar','org/apache/commons/commons-collections4/4.4/commons-collections4-4.4.jar','org/projectlombok/lombok/1.18.36/lombok-1.18.36.jar','org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar','com/alibaba/fastjson/1.2.83/fastjson-1.2.83.jar','jakarta/annotation/jakarta.annotation-api/2.1.1/jakarta.annotation-api-2.1.1.jar','io/swagger/core/v3/swagger-annotations-jakarta/2.2.25/swagger-annotations-jakarta-2.2.25.jar']
for n in ['spring-core','spring-aop','spring-beans','spring-context','spring-expression','spring-jcl']:names.append('org/springframework/'+n+'/6.2.0/'+n+'-6.2.0.jar')
names.append('org/springframework/spring-tx/6.2.1/spring-tx-6.2.1.jar');cp=':'.join(str(m2/n) for n in names);classes=out/('response-binding-'+label);classes.mkdir(exist_ok=True)
r=subprocess.run(['javac','-cp',cp,'-processorpath',str(m2/names[2]),'-d',str(classes),*map(str,files)],capture_output=True,text=True);(out/(label+'-response-binding-compile.txt')).write_text(r.stdout+r.stderr);print('javac_exit',r.returncode)
if r.returncode:print(r.stderr);sys.exit(r.returncode)
r=subprocess.run(['java','-cp',str(classes)+':'+cp,'ResponseBindingProbe'],capture_output=True,text=True);(out/(label+'-response-binding.txt')).write_text(r.stdout+r.stderr+'exit='+str(r.returncode)+'\n');print(r.stdout,r.stderr,'exit=',r.returncode);sys.exit(r.returncode)
