@sreetej510/pi-shipd-checks 0.9.2 → 0.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -91,7 +91,7 @@ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler conf
91
91
  `)}var PMn=900*1e3,hne=["read","grep","find","ls"];function vne(e){return e==="off"?void 0:e}async function Cne(e,t,s=PMn){return t.aborted?"cancelled":new Promise((a,c)=>{let l=!1,u,p=()=>{u!==void 0&&clearTimeout(u),t.removeEventListener("abort",m)},d=S=>{l||(l=!0,p(),a(S))},m=()=>d("cancelled");u=setTimeout(()=>d("timedOut"),s),t.addEventListener("abort",m,{once:!0}),e().then(()=>d("done"),S=>{l||(l=!0,p(),c(S))})})}async function Tne(e){if(e){try{e.isIdle||await e.abort()}catch{}e.dispose()}}async function Oke(e){let t={},s=e.model,a;try{({session:a}=await Ine({cwd:e.tempDir,model:s,thinkingLevel:vne(e.thinkingLevel),tools:[...hne,AT],customTools:[$ke(t)],sessionManager:_ne.inMemory()}));let c=await Cne(async()=>{await a?.prompt(Lke(e.testRubric,e.gapRules,e.fairnessRules,e.codeFiles))},e.cancelSignal,e.timeoutMinutes*60*1e3);if(c!=="done")return await a.abort(),{status:c,gaps:[]}}catch{return{status:"error",gaps:[]}}finally{await Tne(a)}return t.gaps?{status:"ok",gaps:t.gaps}:{status:"noSubmission",gaps:[]}}async function Nke(e){let t={},s=e.model,a;try{({session:a}=await Ine({cwd:e.tempDir,model:s,thinkingLevel:vne(e.thinkingLevel),tools:[...hne,bT],customTools:[kke(t)],sessionManager:_ne.inMemory()}));let c=await Cne(async()=>{await a?.prompt(Gke(e.solverResults,e.testRubric,e.gapRules,e.fairnessRules))},e.cancelSignal);if(c!=="done")return await a.abort(),{status:c,gaps:[]}}catch{return{status:"error",gaps:[]}}finally{await Tne(a)}return t.gaps?{status:"ok",gaps:t.gaps}:{status:"noSubmission",gaps:[]}}async function Vke(e){let t={},s=e.model,a;try{({session:a}=await Ine({cwd:e.tempDir,model:s,thinkingLevel:vne(e.thinkingLevel),tools:[...hne,DT],customTools:[wke(t)],sessionManager:_ne.inMemory()}));let c=await Cne(async()=>{await a?.prompt(Mke(e.solutionRules,e.codeFiles,e.changedCodeDiff))},e.cancelSignal,e.timeoutMinutes*60*1e3);if(c!=="done")return await a.abort(),{status:c,findings:[]}}catch{return{status:"error",findings:[]}}finally{await Tne(a)}return t.findings?{status:"ok",findings:t.findings}:{status:"noSubmission",findings:[]}}var zg=Xn(Ctn(),1),ho=Xn(idn(),1),vr=Xn(rDn(),1),Bee=Xn(Kbn(),1),eEn=Xn(j_(),1),Xy=Xn(Wbn(),1);import{randomUUID as Zbn}from"node:crypto";import{createReadStream as Cxo,existsSync as vAe,mkdirSync as Txo,readFileSync as Axo,rmSync as Ybn,writeFileSync as Fee}from"node:fs";import{tmpdir as Xbn}from"node:os";import{join as km}from"node:path";import{fileURLToPath as Jbn}from"node:url";import{posix as Ixo}from"node:path";function Qbn(e){let t,s="/",a={},c=[];for(let l of hxo(e)){let u=l.match(/^([A-Za-z]+)(?:\s+(.+))?$/s);if(!u)throw new Error(`Invalid Dockerfile instruction: ${l}`);let p=u[1]?.toUpperCase(),d=u[2]?.trim()??"";switch(p){case"FROM":{if(t)throw new Error("Multi-stage Dockerfiles are not supported by the Fargate runner.");let m=Vee(d);if(!m[0]||m.some(S=>S.startsWith("--")||S.toUpperCase()==="AS"))throw new Error(`Unsupported FROM instruction: ${l}`);t=m[0];break}case"WORKDIR":if(!d||d.includes("$")||d.includes("\\"))throw new Error(`WORKDIR must be a concrete POSIX path: ${l}`);s=Ixo.resolve(s,d);break;case"ENV":_xo(d,a);break;case"COPY":if(Vee(d).length<2||Vee(d).some(m=>m.startsWith("--")))throw new Error(`COPY flags and malformed COPY instructions are not supported: ${l}`);break;case"RUN":if(!d||d.startsWith("--")||d.startsWith("["))throw new Error(`Only shell-form RUN instructions without options are supported: ${l}`);c.push(d);break;case"CMD":case"ENTRYPOINT":case"LABEL":case"EXPOSE":break;default:throw new Error(`Unsupported Dockerfile instruction for Fargate execution: ${p}`)}}if(!t)throw new Error("Dockerfile is missing a FROM instruction.");return{baseImage:t,workdir:s,env:a,runtimeCommands:c}}function _xo(e,t){let s=Vee(e);if(s.length===0)throw new Error("ENV requires a variable assignment.");if(s[0]?.includes("=")){for(let a of s){let c=a.indexOf("=");if(c<=0)throw new Error(`Invalid ENV assignment: ${a}`);t[a.slice(0,c)]=a.slice(c+1)}return}if(s.length<2)throw new Error(`ENV requires a value: ${e}`);t[s[0]]=s.slice(1).join(" ")}function hxo(e){let t=[],s="";for(let a of e.split(/\r?\n/)){let c=a.trimEnd();if(!s&&/^\s*#/.test(c))continue;let l=s?`${s}${c.trimStart()}`:c.trimStart();vxo(l)?s=`${l.slice(0,-1).trimEnd()} `:(l.trim()&&t.push(l.trim()),s="")}return s.trim()&&t.push(s.trim()),t}function vxo(e){let t=0;for(let s=e.length-1;s>=0&&e[s]==="\\";s-=1)t+=1;return t%2===1}function Vee(e){let t=[],s="",a,c=!1;for(let l of e)c?(s+=l,c=!1):l==="\\"&&a!=="'"?c=!0:a?l===a?a=void 0:s+=l:l==='"'||l==="'"?a=l:/\s/.test(l)?s&&(t.push(s),s=""):s+=l;if(c&&(s+="\\"),a)throw new Error(`Unterminated quote in Dockerfile instruction: ${e}`);return s&&t.push(s),t}var tEn="shipd-worker",Dxo="fargate-worker.mjs",$C=3300,bxo=5e3;function nEn(e){return new Promise(t=>setTimeout(t,e))}function Exo(e,t){let s=(0,eEn.defaultProvider)({profile:process.env.AWS_PROFILE??t}),a={region:e,credentials:s,maxAttempts:5};return{ecs:new ho.ECSClient(a),ec2:new zg.EC2Client(a),s3:new vr.S3Client(a),sts:new Bee.STSClient(a)}}async function sEn(e){if(e.cancelSignal.aborted)throw new Error("Cancelled by user.");e.onPhase?.("preparing image");let t=km(e.repoDir,"Dockerfile");if(!vAe(t))throw new Error(`Fargate ${e.mode==="solver"?"solver gap finder":"patch precheck"} requires Dockerfile in the project root.`);let s=Qbn(Axo(t,"utf-8")),a=xxo(e.config,e.repoDir),c=process.env.AWS_REGION??a.region??"us-east-1",l=Exo(c,a.awsProfile),u=YAe(e.repoDir,a),p=km(Xbn(),`.shipd-fargate-source-${Zbn()}.tar.gz`),d=$xo(),m=e.mode==="solver"?km(Pxo(),"auth.json"):void 0;if(m&&!vAe(m))throw new Error(`Missing Pi auth file: ${m}`);let S,v,T,x,w=!1,L=()=>{S&&(w=!0,l.ecs.send(new ho.StopTaskCommand({cluster:v,task:S,reason:"shipd checks cancelled"})).catch(()=>{}))};e.cancelSignal.addEventListener("abort",L,{once:!0});let O={source:`runs/${e.runId}/source.tar.gz`,worker:`runs/${e.runId}/${Dxo}`,auth:`runs/${e.runId}/auth.json`,bootstrap:`runs/${e.runId}/bootstrap.json`,result:`runs/${e.runId}/result.json`};try{let G=await kxo(l,a,c);T=G.bucket,v=G.cluster,await wxo(e.pi,e.snapshotDir,p,e.cancelSignal,e.mode==="solver"),await qee(l.s3,G.bucket,O.source,p,"application/gzip"),await qee(l.s3,G.bucket,O.worker,d,"text/javascript"),m&&await qee(l.s3,G.bucket,O.auth,m,"application/json"),x=km(Xbn(),`.shipd-fargate-bootstrap-${Zbn()}.json`);let z=await Mxo(l.ecs,s,u.cpu,u.memoryMiB,c,a),re=e.solverConfig;if(e.mode==="solver"&&!re)throw new Error("Fargate solver configuration is missing.");let ie=e.mode==="solver"?Math.max(1,Math.floor(re?.timeoutMinutes??1)):Math.max(1,Math.floor(e.precheckTimeoutMinutes??20)),xe=e.mode==="solver"?Math.max(1,Math.floor(re?.solverCount??1)):0,be=!!a.taskRoleArn,ve=Math.min(3,Math.max(0,Math.floor(a.maxRetries??1))),ye="Fargate task did not produce a result.";e.onPhase?.("requesting sandbox");let Ne=[];for(let Ue=0;Ue<=ve;Ue+=1){if(e.cancelSignal.aborted)throw new Error("Cancelled by user.");e.onPhase?.("requesting sandbox");let De=await(0,Xy.getSignedUrl)(l.s3,new vr.GetObjectCommand({Bucket:G.bucket,Key:O.worker}),{expiresIn:$C}),tt=be?void 0:await(0,Xy.getSignedUrl)(l.s3,new vr.GetObjectCommand({Bucket:G.bucket,Key:O.source}),{expiresIn:$C}),ce=be||!m?void 0:await(0,Xy.getSignedUrl)(l.s3,new vr.GetObjectCommand({Bucket:G.bucket,Key:O.auth}),{expiresIn:$C}),M=be?void 0:await(0,Xy.getSignedUrl)(l.s3,new vr.PutObjectCommand({Bucket:G.bucket,Key:O.result,ContentType:"application/json"}),{expiresIn:$C}),J=be?void 0:await(0,Xy.getSignedUrl)(l.s3,new vr.GetObjectCommand({Bucket:G.bucket,Key:O.result}),{expiresIn:$C});Fee(x,`${JSON.stringify({workerUrl:De,sourceUrl:tt,authUrl:ce,resultPutUrl:M,resultGetUrl:J,...be?{bucket:G.bucket,region:c,sourceKey:O.source,...m?{authKey:O.auth}:{},resultKey:O.result}:{},mode:e.mode,planB64:Buffer.from(JSON.stringify(s),"utf-8").toString("base64url"),...re?{provider:re.provider,modelId:re.modelId,thinkingLevel:re.thinkingLevel}:{},timeoutMinutes:ie,...e.mode==="solver"?{solverCount:xe}:{},resourceProfile:u.profile})}
92
92
  `,"utf-8"),await qee(l.s3,G.bucket,O.bootstrap,x,"application/json");let Js=await(0,Xy.getSignedUrl)(l.s3,new vr.GetObjectCommand({Bucket:G.bucket,Key:O.bootstrap}),{expiresIn:$C}),Mm={name:tEn,environment:[{name:"SHIPD_BOOTSTRAP_URL",value:Js}]},Wg=await l.ecs.send(new ho.RunTaskCommand({cluster:G.cluster,capacityProviderStrategy:[{capacityProvider:"FARGATE_SPOT",weight:1}],taskDefinition:z,count:1,networkConfiguration:{awsvpcConfiguration:{subnets:G.subnets,securityGroups:[G.securityGroup],assignPublicIp:"ENABLED"}},overrides:{containerOverrides:[Mm]}})),qs=Wg.failures?.[0]?.reason;if(S=Wg.tasks?.[0]?.taskArn,v=G.cluster,!S){ye=qs??"Fargate could not place the Spot task.",e.onPhase?.("requesting sandbox");continue}e.onPhase?.("sandbox created");let Cr=await Oxo(l.ecs,G.cluster,S,()=>Nxo(l.s3,G.bucket,O.result),ie,e.cancelSignal,(nte,dd)=>{e.mode==="solver"&&e.onSolverProgress?.(nte),dd&&e.onResourceUsage?.(dd)},()=>e.onPhase?.(e.mode==="solver"?"running agents":"running patch prechecks"),()=>{w=!0});if(S=void 0,Cr?.resourceUsage&&e.onResourceUsage?.(Cr.resourceUsage),e.mode==="patch-precheck"){if(Cr?.complete&&!Cr.error&&Cr.precheck)return e.onPhase?.("finalizing"),Cr;ye=Cr?.error??ye}else{if(Ne=qxo(Ne,Cr?.results??[]),e.onSolverProgress?.(Ne),Cr?.complete&&!Cr.error&&Ne.length>=xe)return e.onPhase?.("finalizing"),{...Cr,results:Ne};ye=Cr?.error??ye}if(e.cancelSignal.aborted)throw new Error("Cancelled by user.")}return e.mode==="patch-precheck"?{complete:!1,results:[],error:ye}:{complete:!1,results:Fxo(xe,Ne,ye),error:ye}}finally{e.cancelSignal.removeEventListener("abort",L),S&&(w||await l.ecs.send(new ho.StopTaskCommand({cluster:v,task:S,reason:"shipd checks cleanup"})).catch(()=>{}),await Gxo(l.ecs,v,S)),await Bxo(l.s3,T,O).catch(()=>{}),l.ecs.destroy(),l.ec2.destroy(),l.s3.destroy(),l.sts.destroy();try{Ybn(p,{force:!0}),x&&Ybn(x,{force:!0})}catch{}}}async function rEn(e){let t=await sEn({...e,mode:"solver"}),s=await Vxo(e,t.results,e.solverConfig.saveArtifacts);for(let a of s)e.onSolverCompleted?.(a);return s}async function oEn(e){try{let t=await sEn({...e,mode:"patch-precheck"});if(!t.precheck)throw new Error(t.error??"no result was returned");return t.precheck}catch(t){throw new Error(`Fargate patch precheck task failed.
93
93
  platform: linux
94
- ${t instanceof Error?t.message:String(t)}`)}}function xxo(e,t){let s=e.fargate??{},a=s.projectProfiles?.[t];return a?{...s,resourceProfile:a}:s}function Pxo(){let e=Math.max(fc.lastIndexOf("/"),fc.lastIndexOf("\\"));return e>=0?fc.slice(0,e):"."}function $xo(){let t=[Jbn(new URL("./fargate-worker.mjs",import.meta.url)),Jbn(new URL("../dist/fargate-worker.mjs",import.meta.url)),km(process.cwd(),"dist","fargate-worker.mjs")].find(s=>vAe(s));if(!t)throw new Error("Missing dist/fargate-worker.mjs. Build pi-shipd-checks first.");return t}async function wxo(e,t,s,a,c){let p=`tar ${process.platform==="win32"?"--force-local ":""}${c?"--exclude=./solution.patch ":""}-czf ${VC(NC(s))} -C ${VC(NC(t))} .`,d=process.platform==="win32"?"C:\\Program Files\\Git\\bin\\bash.exe":"bash",m=await e.exec(d,["-c",p],{cwd:t,timeout:12e4,signal:a});if(a.aborted)throw new Error("Cancelled by user.");if(m.code!==0)throw new Error(m.stderr?.trim()||"Could not create the Fargate source archive.")}async function qee(e,t,s,a,c){await e.send(new vr.PutObjectCommand({Bucket:t,Key:s,Body:Cxo(a),ContentType:c,ServerSideEncryption:"AES256"}))}async function kxo(e,t,s){let a=await e.sts.send(new Bee.GetCallerIdentityCommand({}));if(!a.Account)throw new Error("Could not determine AWS account identity.");let c=t.bucket??`shipd-checks-${a.Account}-${s}`;await Lxo(e.s3,c,s);let l=t.cluster;if(l||(l=(await e.ecs.send(new ho.ListClustersCommand({maxResults:1}))).clusterArns?.[0]),l||(l=(await e.ecs.send(new ho.CreateClusterCommand({clusterName:"pi-shipd-checks"}))).cluster?.clusterArn),!l)throw new Error("Could not find or create an ECS cluster.");let p=(await e.ec2.send(new zg.DescribeVpcsCommand({Filters:[{Name:"isDefault",Values:["true"]}]}))).Vpcs?.[0]?.VpcId;if(!p)throw new Error("No default VPC found. Configure Fargate subnetIds/securityGroupId explicitly.");let d=t.subnetIds?.length?t.subnetIds:(await e.ec2.send(new zg.DescribeSubnetsCommand({Filters:[{Name:"vpc-id",Values:[p]},{Name:"map-public-ip-on-launch",Values:["true"]}]}))).Subnets?.map(S=>S.SubnetId).filter(S=>!!S)??[];if(d.length===0)throw new Error("No public subnet found for the Fargate task.");let m=t.securityGroupId;if(m||(m=(await e.ec2.send(new zg.DescribeSecurityGroupsCommand({Filters:[{Name:"vpc-id",Values:[p]},{Name:"group-name",Values:["default"]}]}))).SecurityGroups?.[0]?.GroupId),!m)throw new Error("No default security group found.");return{region:s,bucket:c,cluster:l,subnets:d,securityGroup:m}}async function Lxo(e,t,s){try{await e.send(new vr.HeadBucketCommand({Bucket:t}))}catch{await e.send(new vr.CreateBucketCommand({Bucket:t,...s==="us-east-1"?{}:{CreateBucketConfiguration:{LocationConstraint:s}}}))}await e.send(new vr.PutPublicAccessBlockCommand({Bucket:t,PublicAccessBlockConfiguration:{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0}}))}async function Mxo(e,t,s,a,c,l){let u=await e.send(new ho.RegisterTaskDefinitionCommand({family:"pi-shipd-checks-fargate",...l.taskRoleArn?{taskRoleArn:l.taskRoleArn}:{},...l.executionRoleArn?{executionRoleArn:l.executionRoleArn}:{},networkMode:"awsvpc",requiresCompatibilities:["FARGATE"],cpu:String(s*1024),memory:String(a),containerDefinitions:[{name:tEn,image:t.baseImage,essential:!0,command:["bash","-lc",`python -c "import os,urllib.request; urllib.request.urlretrieve(os.environ['SHIPD_BOOTSTRAP_URL'],'/tmp/shipd-bootstrap.json')" && python -c "import json,urllib.request; b=json.load(open('/tmp/shipd-bootstrap.json')); urllib.request.urlretrieve(b['workerUrl'],'/tmp/shipd-fargate-worker.mjs')" && exec node /tmp/shipd-fargate-worker.mjs`],...l.executionRoleArn?{logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":l.logGroup??"/aws/ecs/pi-shipd-checks","awslogs-region":c,"awslogs-stream-prefix":"solver"}}}:{}}]}));if(!u.taskDefinition?.taskDefinitionArn)throw new Error("ECS did not return a task definition ARN.");return u.taskDefinition.taskDefinitionArn}async function Gxo(e,t,s){if(!t)return;let a=Date.now()+12e4;for(;Date.now()<a;){try{if((await e.send(new ho.DescribeTasksCommand({cluster:t,tasks:[s]}))).tasks?.[0]?.lastStatus==="STOPPED")return}catch{return}await nEn(2e3)}}async function Oxo(e,t,s,a,c,l,u,p,d){let m=Date.now()+(Math.max(10,c)+20)*60*1e3,S,v=!1;for(;Date.now()<m;){if(l.aborted)throw d(),await e.send(new ho.StopTaskCommand({cluster:t,task:s,reason:"shipd checks cancelled"})).catch(()=>{}),new Error("Cancelled by user.");let T=await a();if(T&&(S=T,u(T.results,T.resourceUsage),T.complete))return T;let w=(await e.send(new ho.DescribeTasksCommand({cluster:t,tasks:[s]}))).tasks?.[0];if(w?.lastStatus==="RUNNING"&&!v&&(v=!0,p()),w?.lastStatus==="STOPPED"){let L=await a();if(L)return u(L.results,L.resourceUsage),L;let O=w.stoppedReason??w.containers?.[0]?.reason??"Fargate task stopped without a result.";return S??{complete:!1,results:[],error:O}}await nEn(bxo)}return d(),await e.send(new ho.StopTaskCommand({cluster:t,task:s,reason:"shipd checks timeout"})).catch(()=>{}),S??{complete:!1,results:[],error:"Fargate task timed out."}}async function Nxo(e,t,s){try{let c=await(await e.send(new vr.GetObjectCommand({Bucket:t,Key:s}))).Body?.transformToString();return c?JSON.parse(c):void 0}catch{return}}async function Vxo(e,t,s){return t.map(a=>{let{trajectory:c,...l}=a;if(s){let u=km(e.repoDir,iDe,e.runId,`solver_${a.index}`);Txo(u,{recursive:!0}),Fee(km(u,"trajectory.json"),`${JSON.stringify(c??[],null,2)}
94
+ ${t instanceof Error?t.message:String(t)}`)}}function xxo(e,t){let s=e.fargate??{},a=s.projectProfiles?.[t];return a?{...s,resourceProfile:a}:s}function Pxo(){let e=Math.max(fc.lastIndexOf("/"),fc.lastIndexOf("\\"));return e>=0?fc.slice(0,e):"."}function $xo(){let t=[Jbn(new URL("./fargate-worker.mjs",import.meta.url)),Jbn(new URL("../dist/fargate-worker.mjs",import.meta.url)),km(process.cwd(),"dist","fargate-worker.mjs")].find(s=>vAe(s));if(!t)throw new Error("Missing dist/fargate-worker.mjs. Build pi-shipd-checks first.");return t}async function wxo(e,t,s,a,c){let p=`tar ${process.platform==="win32"?"--force-local ":""}${c?"--exclude=./solution.patch ":""}-czf ${VC(NC(s))} -C ${VC(NC(t))} .`,d=process.platform==="win32"?"C:\\Program Files\\Git\\bin\\bash.exe":"bash",m=await e.exec(d,["-c",p],{cwd:t,timeout:12e4,signal:a});if(a.aborted)throw new Error("Cancelled by user.");if(m.code!==0)throw new Error(m.stderr?.trim()||"Could not create the Fargate source archive.")}async function qee(e,t,s,a,c){await e.send(new vr.PutObjectCommand({Bucket:t,Key:s,Body:Cxo(a),ContentType:c,ServerSideEncryption:"AES256"}))}async function kxo(e,t,s){let a=await e.sts.send(new Bee.GetCallerIdentityCommand({}));if(!a.Account)throw new Error("Could not determine AWS account identity.");let c=t.bucket??`shipd-checks-${a.Account}-${s}`;await Lxo(e.s3,c,s);let l=t.cluster;if(l||(l=(await e.ecs.send(new ho.ListClustersCommand({maxResults:1}))).clusterArns?.[0]),l||(l=(await e.ecs.send(new ho.CreateClusterCommand({clusterName:"pi-shipd-checks"}))).cluster?.clusterArn),!l)throw new Error("Could not find or create an ECS cluster.");let p=(await e.ec2.send(new zg.DescribeVpcsCommand({Filters:[{Name:"isDefault",Values:["true"]}]}))).Vpcs?.[0]?.VpcId;if(!p)throw new Error("No default VPC found. Configure Fargate subnetIds/securityGroupId explicitly.");let d=t.subnetIds?.length?t.subnetIds:(await e.ec2.send(new zg.DescribeSubnetsCommand({Filters:[{Name:"vpc-id",Values:[p]},{Name:"map-public-ip-on-launch",Values:["true"]}]}))).Subnets?.map(S=>S.SubnetId).filter(S=>!!S)??[];if(d.length===0)throw new Error("No public subnet found for the Fargate task.");let m=t.securityGroupId;if(m||(m=(await e.ec2.send(new zg.DescribeSecurityGroupsCommand({Filters:[{Name:"vpc-id",Values:[p]},{Name:"group-name",Values:["default"]}]}))).SecurityGroups?.[0]?.GroupId),!m)throw new Error("No default security group found.");return{region:s,bucket:c,cluster:l,subnets:d,securityGroup:m}}async function Lxo(e,t,s){try{await e.send(new vr.HeadBucketCommand({Bucket:t}))}catch{await e.send(new vr.CreateBucketCommand({Bucket:t,...s==="us-east-1"?{}:{CreateBucketConfiguration:{LocationConstraint:s}}}))}await e.send(new vr.PutPublicAccessBlockCommand({Bucket:t,PublicAccessBlockConfiguration:{BlockPublicAcls:!0,BlockPublicPolicy:!0,IgnorePublicAcls:!0,RestrictPublicBuckets:!0}}))}async function Mxo(e,t,s,a,c,l){let u=await e.send(new ho.RegisterTaskDefinitionCommand({family:"pi-shipd-checks-fargate",...l.taskRoleArn?{taskRoleArn:l.taskRoleArn}:{},...l.executionRoleArn?{executionRoleArn:l.executionRoleArn}:{},networkMode:"awsvpc",requiresCompatibilities:["FARGATE"],cpu:String(s*1024),memory:String(a),containerDefinitions:[{name:tEn,image:t.baseImage,essential:!0,linuxParameters:{initProcessEnabled:!0},command:["bash","-lc",`python -c "import os,urllib.request; urllib.request.urlretrieve(os.environ['SHIPD_BOOTSTRAP_URL'],'/tmp/shipd-bootstrap.json')" && python -c "import json,urllib.request; b=json.load(open('/tmp/shipd-bootstrap.json')); urllib.request.urlretrieve(b['workerUrl'],'/tmp/shipd-fargate-worker.mjs')" && exec node /tmp/shipd-fargate-worker.mjs`],...l.executionRoleArn?{logConfiguration:{logDriver:"awslogs",options:{"awslogs-group":l.logGroup??"/aws/ecs/pi-shipd-checks","awslogs-region":c,"awslogs-stream-prefix":"solver"}}}:{}}]}));if(!u.taskDefinition?.taskDefinitionArn)throw new Error("ECS did not return a task definition ARN.");return u.taskDefinition.taskDefinitionArn}async function Gxo(e,t,s){if(!t)return;let a=Date.now()+12e4;for(;Date.now()<a;){try{if((await e.send(new ho.DescribeTasksCommand({cluster:t,tasks:[s]}))).tasks?.[0]?.lastStatus==="STOPPED")return}catch{return}await nEn(2e3)}}async function Oxo(e,t,s,a,c,l,u,p,d){let m=Date.now()+(Math.max(10,c)+20)*60*1e3,S,v=!1;for(;Date.now()<m;){if(l.aborted)throw d(),await e.send(new ho.StopTaskCommand({cluster:t,task:s,reason:"shipd checks cancelled"})).catch(()=>{}),new Error("Cancelled by user.");let T=await a();if(T&&(S=T,u(T.results,T.resourceUsage),T.complete))return T;let w=(await e.send(new ho.DescribeTasksCommand({cluster:t,tasks:[s]}))).tasks?.[0];if(w?.lastStatus==="RUNNING"&&!v&&(v=!0,p()),w?.lastStatus==="STOPPED"){let L=await a();if(L)return u(L.results,L.resourceUsage),L;let O=w.stoppedReason??w.containers?.[0]?.reason??"Fargate task stopped without a result.";return S??{complete:!1,results:[],error:O}}await nEn(bxo)}return d(),await e.send(new ho.StopTaskCommand({cluster:t,task:s,reason:"shipd checks timeout"})).catch(()=>{}),S??{complete:!1,results:[],error:"Fargate task timed out."}}async function Nxo(e,t,s){try{let c=await(await e.send(new vr.GetObjectCommand({Bucket:t,Key:s}))).Body?.transformToString();return c?JSON.parse(c):void 0}catch{return}}async function Vxo(e,t,s){return t.map(a=>{let{trajectory:c,...l}=a;if(s){let u=km(e.repoDir,iDe,e.runId,`solver_${a.index}`);Txo(u,{recursive:!0}),Fee(km(u,"trajectory.json"),`${JSON.stringify(c??[],null,2)}
95
95
  `,"utf-8"),Fee(km(u,"solution.patch"),a.diff,"utf-8"),Fee(km(u,"test_output.txt"),a.testOutputTail,"utf-8"),l.artifactsDir=u}return l})}function qxo(e,t){let s=new Map(e.map(a=>[a.index,a]));for(let a of t)s.set(a.index,a);return[...s.values()].sort((a,c)=>a.index-c.index)}function Fxo(e,t,s){let a=new Map(t.map(c=>[c.index,c]));for(let c=1;c<=e;c+=1)a.has(c)||a.set(c,{index:c,status:"error",passed:!1,diff:"",testOutputTail:s,error:s,durationMs:0,totalTests:null,failedTests:null});return[...a.values()].sort((c,l)=>c.index-l.index)}async function Bxo(e,t,s){t&&await e.send(new vr.DeleteObjectsCommand({Bucket:t,Delete:{Objects:Object.values(s).map(a=>({Key:a}))}}))}var dL="checks_progress";function Uxo(e){return`${Math.max(0,Math.floor(e/1e3))}s`}function aEn(e,t,s,a){let c=a?.showBar!==!1,l=s>0?Math.min(1,t/s):0,u=Math.round(24*l),p="\u2588".repeat(u)+"\u2591".repeat(Math.max(0,24-u)),d=c?` [${p}] ${t}/${s}`:"",m=a?.startedAt===void 0?"":` elapsed: ${Uxo(Date.now()-a.startedAt)}`;return[`checks: ${e}${d}${m}`]}import{existsSync as Hxo,readFileSync as jxo}from"node:fs";var CAe=["agent_prompt.md","solution.patch","test.patch"];function TAe(e=new Date){let t=(l,u=2)=>String(l).padStart(u,"0"),s=-e.getTimezoneOffset(),a=s>=0?"+":"-",c=Math.abs(s);return`${e.getFullYear()}-${t(e.getMonth()+1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}:${t(e.getSeconds())}.${t(e.getMilliseconds(),3)}${a}${t(Math.floor(c/60))}:${t(c%60)}`}function iEn(e){try{if(!Hxo(e))return{};let t=JSON.parse(jxo(e,"utf-8"));return t&&typeof t=="object"&&!Array.isArray(t)?t:{}}catch{return{}}}function cEn(e){let t={...e.existingReport,timestamp:TAe(),model:`${e.config.provider}/${e.config.modelId}`,thinkingLevel:e.config.thinkingLevel};return delete t.overall,delete t.reports,e.runGapFinder&&(t.testGaps=e.testGaps,e.gapAnalysisIncomplete?t.testGapAnalysisNote=`Gap analysis did not fully complete (sentence finder: ${e.gapFinderStatus}, review: ${e.gapFilterStatus}); testGaps may be incomplete.`:delete t.testGapAnalysisNote),e.runSolverGapFinder&&(t.solverRunSummary=e.solverResults.map(s=>({index:s.index,status:s.status,passed:s.passed,durationMs:s.durationMs,totalTests:s.totalTests,failedTests:s.failedTests,error:s.error??(s.status==="error"?s.testOutputTail:void 0),artifactsDir:s.artifactsDir})),t.solverGaps=e.solverGaps,e.fargateResourceUsage?t.fargateResourceUsage=e.fargateResourceUsage:delete t.fargateResourceUsage,e.solverGapAnalysisIncomplete?t.solverGapAnalysisNote=`Solver-gap analysis did not fully complete (comparison reviewer: ${e.solverComparisonStatus}); solverGaps may be incomplete.`:delete t.solverGapAnalysisNote),t}function Kg(e){return`${Math.floor(e/1e3)}s`}function lEn(e){let t=Array.isArray(e.merged.testGaps)?e.merged.testGaps.length:0,s=e.runSolverGapFinder&&Array.isArray(e.merged.solverRunSummary)?e.merged.solverRunSummary.map(l=>({...l,durationMs:l.durationMs??0})):[],a=Array.isArray(e.merged.solverGaps)?e.merged.solverGaps.length:0,c=[];return e.runGapFinder&&c.push(t>0?`${t} test gap(s) found`:"no test gaps found"),e.runSolverGapFinder&&c.push(`${s.filter(l=>l.passed).length}/${s.length} solvers passed, ${a} gap(s) found`),{content:`Gap finders: ${c.join(" | ")}`,details:{gapsCount:t,showGaps:e.runGapFinder,showSolverGaps:e.runSolverGapFinder,solverDetails:s,solverGapsCount:a}}}var zxo=["## The problem description (aka the task)","P1: Aligns with the repo's philosophy.","P2: Self-contained \u2014 solvable from the repo and description alone.","P3: Clear, concise, and unambiguous \u2014 describes what to build or fix without leaving points for guessing.","P4: Verifiable \u2014 success is objectively testable.","P5: Not prescriptive \u2014 does not leak the solution.","","## The tests","T1: They highlight the missing or incorrect behavior and fail before the solution.","T2: They are deterministic and terminating.","T3: They assert the required public behavior strongly and diagnostically.","T4: They cover the requested behavior and justified edge cases.","T5: They do not check unspecified or undiscoverable behavior.","T6: They do not over-pin output, representation, timing, or wording.","T7: They preserve valid alternative implementations and public integration paths.","T8: They keep fixtures, reporting, and the evaluation environment viable.","","## The solution","S1: The solution meets all requirements and preserves required public behavior.","S2: It has no regressions and follows existing repository patterns.","S3: It contains no irrelevant changes.","S4: It contains no dead code, unexplained scaffolding, or avoidable implementation coupling."].join(`
96
96
  `),Kxo=["# Fairness vs. unfairness","","## Core standard","","A test is fair when it checks behavior required by the task description, clearly established by an existing public repository contract, or directly observable by a user or caller. A test is unfair when a reasonable implementation can satisfy the task and the repository's public contract but fail because the test chooses an undocumented implementation detail.","","The reference implementation, a convenient fixture, a neighboring private helper, and a test harness are evidence at most. They are not the specification by themselves.","","For every assertion, ask:","","1. What exact prompt requirement or established public contract supports it?","2. What can a user, caller, or public API observe?","3. Could two competent implementations satisfy that contract while differing on this assertion?","4. Does the fixture and observation point preserve all repository-valid implementation paths?","5. Would the assertion catch a materially incorrect behavior rather than merely a different implementation?","","If two competent implementations can satisfy the contract while differing on the assertion, and the prompt/repository does not select one outcome, the assertion is unfair. Rewrite it around the shared semantic behavior or remove it.","","## Fair","","- Verifies behavior explicitly required by the task.","- Verifies behavior exposed by an existing public API or an established public repository convention.","- Checks the visible result, caller-visible state, required side effect, or documented data contract.","- Requires a new exported name, field, control, attribute, label, key, or option only when the task names it or the existing public API already establishes it.","- Allows equivalent public integration paths, helper delegation, service boundaries, and implementation structures.","- Allows additional valid fields or metadata when the contract requires only a subset of structured output.","- Checks required relationships such as preservation, isolation, ordering, association, or eventual refresh without guessing unrelated concrete values.","- Normalizes or parses equivalent syntax before comparing semantics.","- Accepts clear failure behavior when the contract does not prescribe one exact error representation.","- Waits or polls for an eventual result when synchronous completion is not part of the contract.","- Uses fixtures that preserve real state invariants, module shape, exported references, and valid repository behavior.","- Observes a public effect rather than requiring one internal call or observation point.","- Separates a fair behavioral assertion from an unrelated unsupported co-assertion.","","## Unfair","","### Undocumented implementation choices","","- Requires a private helper, internal class, private state field, or unmentioned file path.","- Requires a particular module boundary, import path, direct platform call, algorithm, call order, or failure-injection primitive when another repository-valid path produces the same effect.","- Requires a new method, constructor, property, export, argument shape, registry key, option ID, or configuration name that the task does not specify and the repository does not already expose publicly.","- Treats the reference implementation's structure, naming, defaults, or minimal serialization as mandatory.","","### Representation instead of semantics","","- Compares an entire object, document, mapping, or array to a minimal expected serialization when the contract requires only selected fields, values, preservation, or relationships.","- Rejects additional valid fields or metadata that do not weaken the required behavior.","- Pins a concrete container or materialization type when the contract specifies the contents or tabular behavior but not the wrapper type.","- Requires a particular field location, source file, interface placement, AST node shape, type alias, type spelling, quote style, whitespace, line break, token adjacency, generated identifier, or code length.","- Requires exact row/group ordering, result cardinality, or per-item counting when the contract does not define that ordering or granularity.","- Requires literal source words to prove behavior that can be checked by compiling, parsing, rendering, executing, or inspecting the public result.","","### Values and relative language","","- Infers an exact value from words such as current, latest, selected, next, matching, appropriate, or stable when only a relationship or effect is required.","- Treats an example, default, neighboring helper behavior, or one fixture value as a universal requirement.","- Requires a particular normalization or encoding strategy when multiple representations preserve the required meaning.","- Requires an incidental no-op artifact, such as whether an empty file, directory, cache entry, history item, or output record exists, when only content/state/reuse/non-duplication is contractual.","","### Counts, categories, and interactions","","- Requires an exact count without a specified counting unit. A count might be per input, field, item, target, rule, match, category, finding, log record, or operation.","- Requires a repair or mutation count without specifying whether it reports findings before the change or findings remaining afterward.","- Assumes diagnostic categories are mutually exclusive or overlapping when the task does not define that relationship.","- Assumes a majority, plurality, unanimity, or other consensus rule without the prompt defining what dissenting values mean.","- Assumes duplicate detection is per collection or global across collections without the contract defining the scope.","- Resolves an interaction between two requirements by intuition\u2014for example, whether a missing optional value skips only value checks or an entire combined rule, whether a cache is reused across a changed input, or whether a target-level check still runs when a value path is absent.","- Requires one line, one repeated term, one log record, or one message per underlying issue when aggregate reporting is equally compliant.","","### Configuration schemas and boundaries","","- Invents a field type, collection shape, omitted-field default, unknown-key policy, coercion rule, extension-based parser dispatch, or cross-field constraint.","- Requires a string-only name, boolean-only flag, particular list schema, strict rejection of extra top-level keys, or rejection of an inverted range when the task does not establish that schema choice.","- Invents acceptance or rejection rules for unspecified malformed, missing, nullish, Unicode, unusually large, boolean-like, numeric, platform-specific, symlink, or missing-parent inputs.","- Assumes a strict validation policy, unlimited policy, coercion policy, or normalization policy that the task does not state.","- Selects one behavior at an underspecified boundary such as dry-run, preview, no-op, conflict, rollback, cache reuse, preflight, or a state transition.","- Assumes mathematical edge values or non-finite values have a particular ordering or rejection policy when the prompt specifies only ordinary finite bounds or numeric behavior.","","### Errors, messages, and timing","","- Requires an exact exception class, error code, message text, punctuation, capitalization, embedded input, basename, or log wording when the contract only requires failure, a clear error, or reporting.","- Requires validation during construction rather than evaluation, or vice versa, when the public contract does not specify timing.","- Requires a callback to return a promise, be fully settled on return, or update immediately when the contract promises only an eventual effect.","- Uses a small wall-clock threshold as a functional requirement without a stated performance contract; such limits are often platform- and load-dependent.","- Requires a particular signal, alarm, subprocess ceiling, poll interval, viewport size, or platform-specific safety mechanism when only bounded or fail-fast behavior is required.","- Requires one exact logging vocabulary when the operation can be reported clearly in equivalent wording.","- Rejects one permitted failure path when the task explicitly allows alternatives such as load-time rejection or evaluation-time violation.","","### Expressions and embedded languages","","- Assumes an unlisted builtin, operator, method, container type, mapping shape, mutability rule, or context binding in a restricted expression language.","- Requires a particular representation for context variables when the task names their meaning but not whether they are mappings, models, lists, tuples, or another equivalent public value.","- Requires a dangerous expression to be rejected during parsing when the contract also permits prohibited constructs to be rejected during evaluation, or vice versa.","- Treats one expression-evaluation cardinality as mandatory when a multi-match rule could reasonably be evaluated once per target, once per rule, or once per resolved value.","","### UI, lifecycle, and harness coupling","","- Requires exact DOM hierarchy, CSS classes, selectors, test/data attributes, native tags, hidden text, hidden-shim text, ARIA attributes, focus order, keyboard traversal, or mock-specific control counts when the task specifies only a user-facing action or visible result.","- Requires an initial focus, default Enter action, Tab order, button order, or immediate control visibility when the prompt only names the available choices.","- Requires a component to expose a particular model API merely so the test can mutate it, when the behavior can be exercised through the real public UI or caller flow.","- Exercises a detached constructor, unmounted widget, partial view, or inactive editor while asserting behavior qualified by an active or mounted production context.","- Uses a mock that omits valid exports, changes the shape of real state, reassigns exported arrays/objects, or patches a source module while the application holds an imported binding.","- Requires a fake collaborator to implement hidden, newly invented hooks instead of using the repository's existing public extension point.","- Uses incomplete fixtures that cause a valid implementation to fail before the behavior under test is reached.","- Accepts only a hidden harness's import path, shim, serialized text, or mock behavior rather than the repository's public contract.","","### Mixed assertions","","A test can contain both fair and unfair assertions. Do not discard the entire behavioral goal automatically. Keep the assertion that checks the required observable effect and remove or relax only the unsupported value, representation, timing, API, side effect, or harness assumption. Conversely, a test that looks behaviorally useful is still unfair overall if one mandatory co-assertion rejects a valid implementation.","","## Scope and alternatives","","Preserve every qualifier in the prompt. A requirement for aware values, naive values, all-day values, one mode, one target state, or one lifecycle phase does not automatically apply to every representation or phase. Do not transfer a rule from one subtype to another, or require a changed wall time, timezone, normalization, or side effect outside the stated scope.","","When the prompt expressly allows more than one outcome or phase, a fair test must accept every permitted alternative. Do not force one load/evaluation phase, one error form, or one integration route merely because it is simpler to assert. Conversely, do not call a test unfair merely because it rejects behavior that the prompt explicitly forbids; distinguish an actual contract prohibition from an unstated mechanism used to enforce it.","","## Public repository evidence","","Existing source code can establish a public contract when the behavior is exposed through a public API, documented UI, established file format, or stable user-facing convention. It does not make every internal branch or helper behavior contractual.","","Use the closest relevant analogues, not an arbitrary nearby example. If analogues disagree, or if the only evidence is private implementation structure, do not pin the detail. A visible security or compatibility rule can make a boundary requirement discoverable, but an unrelated helper's default does not automatically define a new feature.","","A repository convention can justify an exact detail only when it is public, stable, directly relevant, and unambiguous. It does not justify a hidden import-binding assumption, a particular cache mutation path, or a newly introduced collaborator method merely because one implementation uses it.","","## Test strength versus test fairness","","A test may be fair but too weak. For example, a color assertion that searches the entire viewport may pass because an unrelated cell has that color; a message check may find text unrelated to the operation; a timezone assertion may check the destination representation without checking preservation of the underlying instant. These are coverage or observation defects, not reasons to impose a new implementation detail.","","Strengthen such tests by observing the correct public target, associating the result with the operation, and checking the semantic invariant. Do not strengthen them by requiring private state, exact source form, or one architecture.","","A failed setup and a failed behavioral assertion are also different. If a shared fixture crashes before the feature is observed, or an observer renders the wrong widget/viewport after locating a valid result, that is a broken test harness\u2014not evidence of an implementation gap. If the fixture reaches the public behavior but rejects a valid alternative representation or integration path, that is a test/contract mismatch. Neither should be treated as a fair behavioral requirement."].join(`
97
97
  `),Wxo=["# Gaps in tests","","This section defines missing or weak test coverage. It is separate from fairness: a test may be fair but too weak, or a proposed gap may describe a real missing behavior but be unfairly asserted. Use `--GAPS--` records for test-side deficiencies and `T<num>` labels for the test-quality dimension involved.","","## `--GAPS--` candidate rules","","A `--GAPS--` block is a candidate, not an automatic requirement. Accept it only after checking the prompt/public contract, the validity of its fixture, the public observation, and whether a plausible incomplete implementation can evade the current suite. Keep the candidate distinct, actionable, and fair.","","A genuine gap must be:","","- grounded in a specific task requirement or clear public contract;","- distinct from other candidates;","- observable through a public behavior;","- expressible without private implementation assumptions;","- meaningful enough that a plausible incomplete implementation could pass without it; and","- fair to multiple reasonable implementations.","","Do not report optional coverage ideas, generic requests for more negative tests, speculative edge cases, or details that are merely convenient to assert. Edge cases are valuable when the task requires them or the public contract clearly implies them; otherwise they are not gaps.","","When a candidate's first wording is too specific, reformulate it around the semantic requirement. For example, require preservation of required data rather than a minimal serialized object, required ordering relationships rather than an incidental sort order, and a reported failure rather than an arbitrary message or exception path. If no fair formulation remains, drop the candidate.","","An empty candidate list is correct when no proposed check survives these rules.","","## T<num> metrics for test gaps","","`T` metrics describe weaknesses in the tests or test harness. They do not, by themselves, claim that the implementation is wrong. Combine labels when one gap has more than one weakness, for example `T3/T4` for a weak assertion that also misses a required branch.","","### T1 \u2014 fixture and setup reliability","","The test setup must be valid and must reach the behavior under test. Do not count a malformed fixture, missing required outer field, stale object, invalid lifecycle state, incomplete mock, unavailable dependency, or setup exception as an implementation failure. Repair the fixture or isolate the environmental problem while preserving meaningful regression coverage.","","### T2 \u2014 deterministic and terminating execution","","Tests must not depend on wall-clock races, unseeded randomness, scheduler order, loose configurable timeouts, unbounded polling, infinite expressions, deadlocking workers, or machine-dependent thresholds. Use controlled clocks, synchronization, bounded waits, deterministic inputs, and an explicit failure when the bound is exceeded.","","### T3 \u2014 assertion strength and diagnostic specificity","","Assertions must prove the required public result, state, count, relationship, side effect, error channel, or continuation. Avoid label-only output checks, whole-output inequality, stale/unrelated messages, generic exceptions, aggregate flags that hide individual failures, and mixed fixtures that allow a wrong implementation to pass. When diagnostics are contractual, verify that they identify the relevant target/field/rule without pinning exact wording.","","### T4 \u2014 required behavior and edge coverage","","Cover every prompt sentence, explicit prohibition, public entry point, supported backend/format, lifecycle path, and justified boundary. Include meaningful falsey values, aliases, malformed input, empty/missing values, repeated operations, nested/list members, persistence/reload, failure/rollback, and cross-feature interactions when the contract supports them. Do not turn unspecified hypothetical cases into gaps.","","### T5 \u2014 interaction-protocol neutrality","","Drive interactive and UI behavior through the public user contract, not one prompt choreography, ordinal answer sequence, hidden control, private widget, exact call count, or implementation-specific selection protocol. Test the resulting public state/effect and accept equivalent accessible flows unless the prompt fixes the protocol.","","### T6 \u2014 evaluation-environment viability","","The required base/regression command must be runnable in the prescribed offline, non-root, permission, dependency, and repository environment. A broad unrelated suite that fails before reaching the touched area is a harness blocker, not evidence against the solution. Keep genuine touched-area regression tests while removing environmental false failures.","","### T7 \u2014 representation and expectation fairness","","Do not require a reference-only rendering, full untruncated value, exact field placement, parser spelling, DOM structure, output order, or concrete serialization when the public contract permits equivalent results. If a display/format detail is required, assert only that specified semantic detail and use fixtures that do not accidentally test an unrelated truncation or layout limit.","","### T8 \u2014 complete and honest reporting","","Do not use fail-fast execution when the task requires all diagnostics. Every real test must run and appear in the report/JUnit output. Distinguish setup failures, timeouts/OOMs, environment failures, and behavioral assertion failures; never synthesize a collection of failures after a wrapper aborts or let an unrelated warning satisfy the current test.","","## Test-gap calibration from the corpus","","The following rules are distilled from the cleaned unfair-test, gap-report, and rubric corpus. They apply when proposing a test, auditing a reported gap, or judging a solution. They refine the core standard above; they do not create requirements that the task prompt leaves open.","","### Contract-first gap triage","","- A reported gap is actionable only when it identifies a concrete prompt/public-contract requirement, a plausible incomplete implementation that would evade the current tests, and a public observation that distinguishes the behaviors. \u201CMore coverage would be nice\u201D is not a gap.","- During Gap Finder analysis, use `agent_prompt.md` and observable repository behavior as the specification. Do not use `test.patch`, `solution.patch`, a hidden-test name, or reference-only structure to invent a requirement; those may help locate a scenario only after the public contract has independently grounded it.","- Separate three questions: is the behavior required, is the current suite blind to it, and is the proposed test fair? A candidate can be a real missing behavior but still need its assertion rewritten to remove an unfair value, API, timing, fixture, or harness assumption.","- Keep the semantic core when possible. If a candidate fairly proves preservation, isolation, rejection, ordering, or an effect but also pins a message, count, serialization, private method, or prompt sequence, relax only the unsupported co-assertion rather than discarding the whole candidate.","- Do not infer a requirement from solver convergence, the golden solution, a hidden test name, a reference branch, a default chosen by one implementation, or a reviewer\u2019s preferred design. Those are investigation clues only.","- If a candidate exposes a genuine ambiguity\u2014consensus versus plurality, pre- versus post-repair counts, omitted-default semantics, cross-format collision policy, preset precedence, hunk grouping, or a recurrence alias grammar\u2014clarify the prompt or accept all reasonable outcomes. Do not silently turn one interpretation into a hidden requirement.","- Negative tests require an actual prohibition, guard, invalid state, isolation rule, or failure policy. Do not turn ordinary positive coverage into a negative gap merely because a second wrong implementation is imaginable.","","### Assertion strength without unfairness","","- Do not use whole-output inequality, \u201Csome output changed,\u201D \u201Csome warning exists,\u201D a nonempty exception, or a generic `has_issues` flag as the only proof of a required behavior. Assert the relevant public state, result, count, continuation, or effect directly.","- When a contract defines counts, parse the complete count lines and assert the correct count. When it does not define the counting unit, assert detection and state/effect, or first define whether the unit is a field, item, rule, target, match, category, or operation.","- Do not require one log record per issue, one repeated keyword, or one physical line per item unless the prompt specifies that granularity. Conversely, do not allow a single aggregate result to hide required independent findings when the prompt explicitly says to count/log each violation.","- Test positive and negative cases independently. A mixed fixture with one valid and one invalid value can prove only that something happened; add an isolated valid case and an isolated invalid case when acceptance of the valid case matters.","- For aliases, options, fields, formats, supported types, and backends, test each promised category or a justified representative matrix. Do not let a hard-coded subset pass because the visible examples cover only four fields, one backend, one selector, one alias, or one error path.","- For multi-item or multi-field operations, assert that all required members are processed, that valid neighbors continue after a skippable invalid member, and that one failure does not silently short-circuit later required checks.","- When testing a warning or diagnostic, capture the repository\u2019s actual public channel and associate the message with the operation. Do not accept an unrelated log record, and do not require a literal word, exact field name, basename, punctuation, or stdout when the contract only requires a clear diagnostic.","- If a message must identify a rule, target, field, or path, accept the contract\u2019s alternatives (name, index, or equivalent location) and do not assume zero-based indexing, a dotted path, a particular vocabulary, or a fixed output layout.","","### Harness and fixture integrity","","- A test setup must be valid under the repository\u2019s real public lifecycle. Do not create state outside the invocation/session that the prompt says owns it and then require an invocation-scoped recorder to observe it. Do not use stale model objects after the contract says pruning/removal permanently deletes them.","- Do not omit required outer fields, use an invalid document shape, or combine an unrelated malformed field with the behavior under test. For example, a JSON test requiring successful import must include every prompt-required top-level list, and an ICS success fixture must be valid independently of the RRULE edge being tested.","- Do not make a fake module or collaborator less capable than the real public object. Preserve valid exports, attributes, method signatures, model shapes, iterable/generator behavior, and state invariants. A fake that drops new fields, returns a list where the real API returns a generator, or changes import identity can reject a correct solution.","- Patch the public boundary actually used by the application, or use a real isolated integration path. Do not patch `module.function` while the implementation may have a direct imported binding, patch one of several invented import paths, require a nonexistent future helper, or make success depend on a private `get_templates_dir`, loader, saver, or factory name.","- A test must not require a specific pool API (`apply_async` versus `imap_unordered`), callback protocol, executor, scheduler, import style, or worker placement. Test prerequisite ordering, blocking, isolation, and failure propagation through a fair public boundary. Use deterministic latches for concurrency rather than inspecting a private `pending` map.","- Do not drive a new UI by an ordinal iterator of answers, prompt keyword matching, exact confirmation-call count, or hard-coded free-text token unless that protocol is explicitly specified. A compliant implementation may use a menu, free text, a delimited selection, a separate confirmation, or another accessible flow.","- Do not assume a detached constructor, unmounted screen, inactive editor, or fake collection has the same lifecycle effects as the production path. If the requirement is \u201Cwhile active,\u201D open it through the real public path; if the requirement is a model behavior, test the model directly.","- Shared helpers must not contain hidden assumptions that contaminate every test: fixed path lengths, presumed default filenames, required absolute paths, a particular editor implementation, a specific button traversal order, or a default format that the prompt does not define.","","### Public UI and interaction checks","","- Check visible, usable behavior through accessible labels, public actions, resulting state, and actual requests/effects. Do not count inert text in a script/template block as a rendered control, and do not require a particular DOM/widget class, layout, query order, coordinate, or internal model key.","- Locate controls by stable public role/label or exercise them through user-visible navigation. Do not require a specific initial focus, Enter default, Tab order, Up/Down key, row order, button order, or that a successful action keeps a screen mounted unless the prompt or an established UI contract says so.","- For selection tests, identify the intended row/action by its visible identity rather than assuming the second row is selected after one particular key. Assert both directions when the prompt requires both, and verify the selected item\u2019s public identity, not merely the row index.","- For status/error tests, clear or snapshot prior status so stale text cannot satisfy a later assertion. Capture stdout, stderr, structured events, and user-facing notifications according to the repository contract rather than choosing one stream arbitrarily.","- For machine-readable output, parse events/fields structurally. Do not require a field to be top-level, an array rather than a mapping, a particular error key, a traceback key, a bytes-count interpretation, or a welcome payload flattening unless the prompt explicitly states that shape.","- For destructive dialogs, assert that the warning communicates the required consequence and that Confirm/Cancel effects are correct. Do not require exact explanatory prose, initial focus, or a particular widget type.","","### Serialization, formats, and structured data","","- Assert required semantic fields and relationships while allowing valid extra fields, metadata, headers, separators, and equivalent serialization. Do not compare an entire object to the reference\u2019s minimal form.","- Do not assume an omitted option has a particular default format, algorithm, encoding, extension, output destination, or parser. Pass the option explicitly when the test is about another behavior, or state the default in the prompt before testing it.","- Do not require a headerless file, zero physical lines, one exact newline form, lowercase digest hex, one field order, one RRULE prefix/case/order, or one Markdown/Taskpaper syntax when the contract permits equivalent representations. Parse the result and compare meaning.","- When a format contract does require a schema, test the schema itself: required top-level fields, field types, list element types, valid/invalid enum values, round-trip preservation, and malformed stored data. Do not accidentally accept a missing required field or test only data created by the same implementation.","- Test create, edit, load/show, list, validate, and externally corrupted storage separately when each is public. A create-time validator does not prove that `validate` reads stored corruption, and a parser that saves a file does not prove that a later process can reload it.","- Failed edits, overwrite attempts, cross-format replacement, serialization failures, and editor cancellation must preserve the last valid representation when the prompt promises non-destructive behavior. Verify by reopening/reloading, not only by checking the immediate error.",'- For round trips, include falsey but meaningful values (`0`, `False`, `""` when allowed), `None` where distinct, non-ASCII text, malformed nested types, duplicate rows, optional fields, and fields with metadata. Avoid truthiness shortcuts in both implementation and assertions.',"","### Validation, numeric, and schema boundaries","","- Test all required enforcement surfaces: direct assignment, construction/addition, modification, defaults, collection validation, CLI overrides, interactive prompts, serialization, reload, and each supported model type. Do not infer that one path covers another.","- Distinguish missing, explicit null, blank/whitespace, empty container, false, zero, NaN, infinity, and a textual representation of each. A truthiness check must not reject valid `False`/`0` or accept malformed falsey input.","- If the prompt requires finite numeric bounds or rejects numeric NaN, test both positive/negative infinity and NaN independently against minimum and maximum, including list members and configured bounds. Do not use ordinary comparison behavior as an unstated policy for non-finite values.","- Use base-type normalization consistently for enum choices, bounds, defaults, native values, JSON values, BLOB/text values, aliases, dates, durations, and nullable numeric types. Test fractional numeric strings, epoch dates, boolean-valued options, Unicode case behavior, and canonicalization only when the prompt defines those semantics.","- If invalid schemas must fail at construction, assert construction-time failure; if the prompt allows eager or lazy validation, accept either by triggering and observing the first required validation boundary. Do not accidentally test only one timing.","- Test nested schemas recursively: native and JSON roots, nested JSON strings, typed list/dict descriptors, declared fields, dynamic dictionaries, required nested containers, defaults, invalid enum/schema names, cycles versus shared DAGs, depth limits, clone independence, empty explicitly supplied schemas, and CLI override integration.","- Keep schema syntax and field grammar explicit. Do not invent bare mappings, sibling `type/schema` keys, reserved-field handling, enum aliases, extra top-level keys, or omitted-required defaults in a test unless the prompt states them.","","### Persistence, transactions, and state isolation","","- For every mutating operation, snapshot the important state before and after: database/model values, media/files, selected configuration, history, caches, output trees, and unaffected siblings. Unknown IDs, skipped entries, canceled actions, and invalid input must not mutate unrelated state.","- Test all-or-nothing behavior across phases, not only a failure in the first phase. Force a later write to fail after an earlier repair/import/override has changed state, then use a fresh reader to verify complete rollback.","- Test continuation where the contract says to skip one bad record and continue, including valid records before and after missing/invalid records. Do not accept a warning as proof that valid work was restored/imported.","- Test no-op semantics independently: duplicate additions should not create data or undo/history entries; identical content should not cause unnecessary writes unless the contract requires a write; empty values should not be confused with omitted values.","- Persist and reload through a fresh process/session/backend. A same-process object or a monkeypatched in-memory store can hide missing serialization, stale cache, remote-backend, or lifecycle behavior.","- Cache tests must vary the requested input/path and invocation order. Do not let a stale cached inventory, profile, schema, or preset make a second operation observe the first operation\u2019s data.","- Backend-neutral history/snapshot tests must include complete remote state and backend-specific settings, not merely a serialized local reference/locator. Verify retention, sidecars, digests, encoding metadata, rollback, and update-target paths without requiring a private snapshot format.","","### Paths, filesystems, archives, and security","","- Test both lexical and resolved containment. Reject absolute paths, dot-segment escapes, drive/UNC forms, alternate separators, NUL/control forms, and symlink traversal through an in-root link to an outside location. Preserve existing outside files and avoid publishing partial trees.","- Apply confinement consistently to list/read/create/update/delete, history tokens, profile/template names, playlist paths, archive members, extraction destinations, and selected archive views. A safe lexical path can still escape through a filesystem symlink.","- Do not require safe inputs to be transformed into one particular spelling. For safe symlinks, backslashes, dot segments, or encoded names, compare resolved containment and semantic output unless the prompt defines canonical normalization. Separately test malicious forms where rejection is required.","- For archive manifests, test manifest/archive bijection, type/path/size/mode/digest schemas, duplicate and normalized-collision paths, symlink target agreement, counters, ordering independence, malformed ZIPs, path hierarchy conflicts, and no-partial-extraction cleanup. Do not infer array-vs-map manifest shape or duplicate archive rejection unless specified.","- Test missing, malformed, unreadable, externally corrupted, and symlinked storage. A valid filename or index name must not be treated as corruption merely because it matches a test fixture\u2019s naming pattern.","- Preserve encoding and text semantics. State UTF-8 or another codec before testing invalid bytes; include successful Unicode round trips as well as invalid-byte failures. Do not treat a valid U+FFFD filename as undecodable merely because it contains the replacement character.","","### Time, recurrence, and platform-sensitive behavior","","- Freeze or inject the clock for completion, expiration, age, timestamp, retry, and schedule tests. Never compare against independently sampled wall-clock values across a possible midnight/second boundary.","- Test strict-after equality, due-before/due-after completion, month-end clamping, leap days, original calendar anchors across repeated cycles and reload, COUNT/UNTIL preservation and exhaustion, multiple comma-separated selectors, and all-day/aware/floating timezone behavior when the prompt promises them.","- Preserve the prompt\u2019s qualifier. A requirement about existing aware values does not automatically impose a changed wall time on floating values; a rule about actual completion time does not justify a pre-call timestamp; a rule about one format does not define another format\u2019s error policy.","- Compare recurrence schedules semantically or parse structured fields. Do not require an exact RRULE prefix, case, field order, minimal field set, natural-language alias spelling, formatter vocabulary, microsecond precision, or hidden anchor property unless explicitly specified.","- Test both model and editor surfaces, persistence/reload, save/discard/undo, unsupported valid rules, destructive confirmation content, dirty-state propagation, and visible calendar highlighting through the real active view. Do not mutate a detached model and call that proof of UI synchronization.","","### Restricted expressions and resource limits","","- A \u201Cread-only\u201D expression context must be genuinely non-mutable or mutation attempts must be rejected and reported according to the prompt. A deep copy alone is isolation, not immutability.","- Test all documented bindings (`target`, `inventory`, `value`, `values`, target name) with successful reads, including wildcard and recursive matches. Do not assume mapping/list shapes, builtin availability, evaluation cardinality, or per-member count unless specified.","- Test prohibited imports, file access, dangerous builtins, mutation through every binding, evaluation errors, recursion, infinite iterators, C-level loops, and later-rule isolation. A trace hook alone may not interrupt C-level work; use a process/resource boundary or reject dangerous constructs at load time if the prompt allows either.","- Any pathological-expression test must have an unconditional finite timeout and a deterministic failure path. It must not hang, OOM, or rely on an externally configured environment variable to become safe.","","### Test runner and reporting integrity","","- Never use `--maxfail=1` or another fail-fast mode when the task requires complete diagnostics. Every real test must execute and appear in JUnit/report output; do not synthesize \u201Cmissing test\u201D failures after a wrapper aborts.","- Bound every test-side growth loop, retry loop, subprocess, poll, and concurrency wait. If the required marker/result is not observed by the bound, fail that test with an explicit cause.","- Keep test diagnostics honest. Separate infrastructure/setup failure, timeout/OOM, and implementation assertion failure. Do not classify a killed process as every individual test failing, and do not let an unrelated warning or stale status satisfy the current operation.","- Avoid machine-dependent thresholds, platform-specific permissions, real network, mutable global state, and unseeded randomness unless the prompt explicitly requires them and the test controls the environment.","","### Gap-finder recurring fair-gap checklist","","When auditing a suite for missing behavioral coverage, deliberately consider these public branches when the prompt supports them:","","- noncanonical but meaningful values, falsey malformed values, Unicode/case variants, NaN/infinity, empty/zero values, nested and repeated inputs;","- every public entry point and lifecycle: constructor/add/modify, direct/JSON/native, interactive/non-interactive, CLI/TUI, create/edit/load/show/list/validate, fresh reload/process, and all supported backends/formats;","- defaults, aliases, requiredness, retry locality, blank versus interruption, cancellation/resume, duplicate no-ops, and invalid input side effects;","- all declared fields rather than a hard-coded subset, all selector values/grammar branches, all target/path scopes, all independent siblings, and all transitive dependencies;","- resolved data rather than raw files, cache misses and stale-cache changes, target identity versus display aliases, dotted/cross-platform names, and selected-target closure scope;","- public counts, per-target attribution, summary completeness, structured diagnostics, correct output channels, and actual visible controls/effects rather than labels alone;","- malformed stored data, failed later phases, atomic rollback, cross-format replacement, unknown IDs, missing records, remote/backend-specific state, sidecar integrity, and no partial artifacts;","- real parallel execution, dependency gating, fetch ordering, failure propagation, deterministic public order/graph output, and cleanup of threads/processes/temporary files;","- archive/playlist/template/profile path safety, symlink containment, separator variants, archive-manifest agreement, encoding, and successful Unicode paths;","- recurrence/calendar anchor, strict-time, selector-list, COUNT/UNTIL, all-day/aware/floating, save/discard/undo, and active-UI highlight branches.","","For each candidate, still apply the fairness screen: the edge must be required or clearly public, the fixture must be valid, the observation must be public, and the assertion must not smuggle in a count unit, parser spelling, UI protocol, private API, harness shape, or timing threshold."].join(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sreetej510/pi-shipd-checks",
3
- "version": "0.9.2",
3
+ "version": "0.9.3",
4
4
  "description": "Pi extension that runs a strict, multi-agent fairness review of a benchmark task's agent_prompt.md, test.patch, and solution.patch, plus behavioral test-gap analysis, via /checks.",
5
5
  "type": "module",
6
6
  "license": "MIT",