@sreetej510/pi-shipd-checks 0.9.8 → 0.9.10
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.
- package/README.md +9 -10
- package/dist/fargate-worker.mjs +1 -1
- package/dist/index.js +57 -57
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -143,14 +143,14 @@ On-Demand fallback. Spot interruptions are retried according to `fargate.maxRetr
|
|
|
143
143
|
```
|
|
144
144
|
|
|
145
145
|
`cluster`, `subnetIds`, and `securityGroupId` are optional when a default VPC is available.
|
|
146
|
-
With `adaptiveResourceProfile: true`,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
`projectProfiles` to override
|
|
153
|
-
`{"C:/path/to/repo":"large"}`.
|
|
146
|
+
With `adaptiveResourceProfile: true`, solver-gap runs use `resourceProfile` initially and adapt
|
|
147
|
+
on later runs (unless `projectProfiles` already overrides the repository). They upgrade when
|
|
148
|
+
normalized CPU is at least 95% for more than seven minutes, downgrade when CPU is at least 95%
|
|
149
|
+
for under two minutes, and otherwise retain the profile. Only the selected next profile is
|
|
150
|
+
written to `projectProfiles`; CPU telemetry is included in `shipd_report.json` for solver-gap
|
|
151
|
+
runs, but no telemetry history is retained. Quality patch prechecks always use the `medium`
|
|
152
|
+
profile and do not update adaptive sizing. Set `projectProfiles` to override solver-gap
|
|
153
|
+
resources per repository: `{"C:/path/to/repo":"large"}`.
|
|
154
154
|
|
|
155
155
|
5. Restart pi, use `/checks --config` to select the solver and reviewer models, then run
|
|
156
156
|
`/checks --solver-gap-finder`. Projects need `Dockerfile`, `agent_prompt.md`,
|
|
@@ -168,8 +168,7 @@ credential from the local pi auth state for the task; never put the credential i
|
|
|
168
168
|
Use `/analyze:on` and `/analyze:off` to control the gap-finder and solution-precheck tools per project, like HPC. The
|
|
169
169
|
enabled project list is stored alongside the other settings in `~/.pi/agent/checks-config.json`.
|
|
170
170
|
|
|
171
|
-
The patch precheck applies `test.patch`, creates isolated workspaces with and without `solution.patch`, and runs the
|
|
172
|
-
base suites concurrently in both workspaces followed by the new suites concurrently in both workspaces. It requires
|
|
171
|
+
The patch precheck always uses the `medium` Fargate profile. It applies `test.patch`, creates isolated workspaces with and without `solution.patch`, and runs the base suites concurrently in both workspaces followed by the new suites concurrently in both workspaces. It requires
|
|
173
172
|
`./test.sh base` to pass, requires every `./test.sh new` testcase to fail or error individually (with no suite-level error)
|
|
174
173
|
before `solution.patch`, then requires both base and new suites to pass after the solution patch. A failed precheck aborts
|
|
175
174
|
before Shipd is opened and includes the Linux platform, a human-readable phase, and failed/errored test names in the tool error.
|
package/dist/fargate-worker.mjs
CHANGED
|
@@ -2364,7 +2364,7 @@ Options:
|
|
|
2364
2364
|
`)}var Q9e=class{constructor(e,n){this.pi=e;this.options=n;this.now=n.now??Date.now,this.logWarning=n.logWarning??(i=>console.warn(`[commandcode] ${i}`));let r={transport:"unknown",source:"empty",modelCount:0,cachePath:n.cachePath,endpoint:n.endpoint,refreshing:!1};this.status={...r}}now;logWarning;status;providerRegistered=!1;refreshPromise;getStatus(){return{...this.status,transport:this.options.getTransport?.()??"unknown"}}async initialize(){this.registerCommands(),await this.refresh()}refresh(){if(this.refreshPromise)return this.refreshPromise;let e=this.refreshCatalog().finally(()=>{this.refreshPromise===e&&(this.refreshPromise=void 0)});return this.refreshPromise=e,e}async refreshCatalog(){this.status={...this.status,lastAttempt:this.now(),refreshing:!0};try{let e=await this.options.loadModels(),n=e.warning?Zbe(e.warning):void 0;if(!this.providerRegistered||e.source==="live"||this.status.modelCount===0&&e.models.length>0){if(this.pi.registerProvider("commandcode",this.options.createProviderConfig(e.models)),this.providerRegistered=!0,e.models.length===0){let s=n??"Model catalog refresh returned no models";return this.status={...this.status,source:e.source,modelCount:0,warning:s,refreshing:!1},this.warn(s),{refreshed:!1,source:e.source,modelCount:0,warning:s}}return this.status={...this.status,source:e.source,modelCount:e.models.length,lastSuccess:this.now(),warning:n,refreshing:!1},n&&this.warn(n),{refreshed:!0,source:e.source,modelCount:e.models.length,warning:n}}let i=n??"Model catalog refresh returned no models";return this.status={...this.status,warning:i,refreshing:!1},this.warn(i),{refreshed:!1,source:this.status.source,modelCount:this.status.modelCount,warning:i}}catch(e){let n=Zbe(`Could not refresh the Command Code model catalog: ${aOi(e)}`);return this.status={...this.status,warning:n,refreshing:!1},this.warn(n),{refreshed:!1,source:this.status.source,modelCount:this.status.modelCount,warning:n}}}warn(e){try{this.logWarning(Zbe(e))}catch{}}registerCommands(){this.pi.registerCommand("commandcode-refresh",{description:"Refresh the Command Code model catalog",handler:async(e,n)=>{await n.waitForIdle?.();let r=await this.refresh();r.refreshed?n.ui.notify(`Command Code model catalog refreshed (${r.modelCount} models from ${r.source}).`,"info"):n.ui.notify(`Command Code model catalog unchanged (${r.modelCount} models remain available).${r.warning?` ${r.warning}`:""}`,"warning")}}),this.pi.registerCommand("commandcode-status",{description:"Show redacted Command Code provider diagnostics",handler:async(e,n)=>{let r=this.getStatus();n.ui.notify(lOi(r),r.warning?"warning":"info")}})}};function JPn(t,e){return new Q9e(t,e)}function XPn(t){return typeof t=="object"&&t!==null&&!Array.isArray(t)}async function uOi(t){if(t.status!==403)return!1;try{let e=await t.clone().json();return XPn(e)?(XPn(e.error)?e.error:e).code==="upgrade_required":!1}catch{return!1}}function ZPn(t){let e="unknown",n;function r(i,s){return(async()=>{for await(let o of i)s.push(o)})()}return{getTransport(){return e},reset(){e="unknown",n=void 0},stream(i,s,o){o?.apiKey!==n&&(n=o?.apiKey,e="unknown");let a=o?.apiKey;if(e==="generate")return t.streamGenerate(i,s,o);let c=t.createStream(),u=!1,l=o?.fetch??fetch,p={...o,fetch:async(m,h)=>{let f=await l(m,h);return await uOi(f)&&(u=!0),f},onResponse:async(m,h)=>{u||await o?.onResponse?.(m,h)}};return(async()=>{let m=t.streamProvider(i,s,p);for await(let h of m)u||(n===a&&(e="provider"),c.push(h));u&&(n===a&&(e="generate"),await r(t.streamGenerate(i,s,o),c)),c.end()})().catch(m=>{let h=m instanceof Error?m.message:String(m);c.push({type:"error",reason:"error",error:{role:"assistant",content:[],api:i.api,provider:i.provider,model:i.id,usage:{input:0,output:0,cacheRead:0,cacheWrite:0,totalTokens:0,cost:{input:0,output:0,cacheRead:0,cacheWrite:0,total:0}},stopReason:"error",errorMessage:h,timestamp:Date.now()}}),c.end()}),c}}}function tNn(){if(process.env.CMD_ZDR==="1"||process.env.COMMANDCODE_ZDR==="1")return{"x-cmd-zdr":"1"}}function dOi(t,e,n){let r=tNn();return{name:"Command Code",baseUrl:e,apiKey:Ube()??"$COMMAND_CODE_API_KEY",api:"commandcode-custom",streamSimple:n,headers:r,oauth:{name:"Command Code",login:QPn,refreshToken:GPn,getApiKey:qPn},models:t.map(i=>({id:i.id,name:i.name,api:"commandcode-custom",baseUrl:rPn(e,i.api),reasoning:i.reasoning,...tPn(i.id)??{},input:[...v9e(i.id)],cost:HPn[i.id]??WPn,contextWindow:i.contextWindow,maxTokens:i.maxTokens,headers:r,compat:i.api==="openai-completions"?{supportsStore:!1,supportsDeveloperRole:!1,supportsReasoningEffort:$be[i.id]!==void 0,maxTokensField:"max_tokens"}:{supportsEagerToolInputStreaming:!1,supportsLongCacheRetention:!1,supportsCacheControlOnTools:!1,supportsToolReferences:!1,...i.reasoning?{forceAdaptiveThinking:!0}:{}}}))}}function eNn(t){return t.replace(/\/provider\/v1\/?$/,"")}async function nNn(t){let e=process.env.COMMANDCODE_API_BASE??I9e,n=process.env.COMMANDCODE_MODELS_URL??w9e,r=aPn(),i=process.env.COMMANDCODE_MODELS_CACHE??pOi(Lr(),"commandcode-models.json"),s=NPn({createStream:()=>new ua,calculateCost:OPn,apiBase:eNn(e)}),o=ZPn({createStream:()=>new ua,streamProvider:(c,u,l)=>c0({...c,api:qbe(c.id),compat:c.compatConfig??c.compat},u,l),streamGenerate:s});t.on("message_end",async(c,u)=>{if(c.message.role!=="assistant")return;let l=J1n(c.message,u.model?.provider);return l?{message:l.message}:void 0}),KPn(t,{apiBase:eNn(e),headers:tNn()}),await JPn(t,{endpoint:n,cachePath:i,loadModels:()=>cPn({url:n,cachePath:i,timeoutMs:r}),createProviderConfig:c=>dOi(c,e,o.stream),getTransport:o.getTransport}).initialize()}import{dirname as u5a,join as rNn}from"node:path";var d5a=rNn(Lr(),"checks-config.json"),m5a=rNn(Lr(),"settings.json");var f5a=420*1e3,h5a=120*1e3;var w5a=ji.Object({verdict:ji.Union([ji.Literal("PASS"),ji.Literal("FAIL")],{description:"FAIL only for a genuine blocking issue (clear rubric violation, agent-fault-worthy gap, unfair/undiscoverable test requirement, or a real determinism/regression risk). Optional, minor, or stylistic points are NOT grounds for FAIL \u2014 put those in `notes` instead and use PASS."}),summary:ji.String({description:"One short sentence summarizing the verdict."}),reasons:ji.Array(ji.String(),{description:"Specific BLOCKING justifications only, citing rubric item IDs and concrete evidence from the files you read. Required (non-empty) when verdict is FAIL. Use an empty array when verdict is PASS."}),notes:ji.Array(ji.String(),{description:"Non-blocking, optional/minor observations or suggested improvements \u2014 the kind of feedback a real reviewer leaves as 'Minor/optional' without failing the task. Include these regardless of verdict; use an empty array if you truly have none."})});var v5a=ji.Object({gaps:ji.Array(ji.Object({description:ji.String({description:"A fair, publicly observable missing behavioral test."}),justification:ji.String({description:"Why the gap is grounded in agent_prompt.md or the repository and why a plausible incorrect implementation could pass the current tests despite missing this behavior."})}),{description:"The complete final list of distinct, evidence-backed positive and negative behavioral test gaps. Use an empty array only after exhaustive analysis finds none."})});var fOi=ji.Object({category:ji.Union([ji.Literal("missing-requirement"),ji.Literal("regression"),ji.Literal("architecture"),ji.Literal("unsafe-failure"),ji.Literal("inconsistent-path"),ji.Literal("dead-code"),ji.Literal("unrelated-change")],{description:"The kind of actionable quality problem found in the solution implementation."}),subject:ji.String({description:"A short behavior, symbol, or concern identifier; do not require a source line or file location."}),problem:ji.String({description:"What is wrong with the implementation and why it is actionable."}),evidence:ji.String({description:"The prompt, repository, implementation, or regression evidence supporting the finding."}),requiredBehavior:ji.String({description:"The requirement, invariant, or solution-quality property that must be preserved."}),recommendation:ji.String({description:"A concrete repair that stays within the prompt and repository conventions."})}),R5a=ji.Object({findings:ji.Array(fOi,{description:"Only confirmed, actionable solution-quality findings. Use an empty array when the implementation meets the prompt and repository standards."})});var k5a=ji.Object({gaps:ji.Array(ji.Object({description:ji.String({description:"The specific behavioral gap: a way one or more solver's diff differs materially from what agent_prompt.md/solution.patch require, despite that solver passing `./test.sh new`."}),justification:ji.String({description:"Why this is a genuine requirement from agent_prompt.md or solution.patch (cite the specific requirement/line) that the solver's diff misses, contradicts, or diverges from \u2014 and why the current tests fail to catch that divergence."}),evidence:ji.String({description:"Which solver(s) (by index) exhibit this, and the specific part of their diff that grounds the gap."})}),{description:"Concrete, diff-grounded behavioral gaps \u2014 cases where a passing solver's materially different approach reveals that `test.sh new` under-specifies a real requirement. Use an empty array if the solvers converged on equivalent behavior; do not manufacture gaps just to report something."})});function iNn(){return["You are a adversarial tester. Your goal is to find the most minimal, shortcut implementation that makes all new tests pass \u2014 not a full, correct solution. This is intentional: we want to discover whether the test suite lets through an implementation that barely satisfies the assertions without properly implementing the prompt's required behavior.","`agent_prompt.md` in the repo root describes the intended task. Tests for it already exist and are currently failing.","","Goal: make ALL of the new tests pass with the least amount of real logic possible.","1. Run `bash test.sh new` to see the failures and what the tests expect. Read the failing tests carefully \u2014 they are the only spec you need to satisfy.","2. Understand exactly what each test assertion checks. Ask, 'what is the minimum thing I can do \u2014 a stub, a hardcoded return, a mockish pass-through, a trivial branch \u2014 to satisfy this test?', not 'what does agent_prompt.md intend?'. Skim `agent_prompt.md` only enough to understand what the test file names and assertions refer to, then rely on the test errors to drive what to implement.","3. Prefer the most minimal implementation possible:"," - Hardcode a return value if the test expects one."," - Add only the code paths a test actually exercises; leave anything untested unimplemented."," - Mock the minimum number of dependencies (or none) that the test error forces you to touch."," - Use the most direct, trivial, shortcut approach \u2014 do not build abstractions, do not handle unobserved edge cases, do not add defensive code.","4. Before declaring success, also run `bash test.sh base` to confirm your changes cause no regressions. The shortcut must not break existing tests \u2014 that would be a false positive from a different angle.","5. Re-run `bash test.sh new` and iterate until both the new tests and the base tests pass, or you're confident you cannot pass more without adding real logic.","","Rules:","- Do NOT modify the test files or `test.sh`. Only change application/library code.","- You have shell access \u2014 install any dependencies you need.","- The prompt is not your spec: the tests are. Implement exactly what the tests demand, nothing more. If you can satisfy a test with a hardcoded return, do it."].join(`
|
|
2365
2365
|
`)}import{readFileSync as sNn}from"node:fs";var hOi=5e3,gOi=95;function AOi(t){try{let e=Number.parseInt(sNn(t,"utf-8").trim(),10);return Number.isFinite(e)?e:void 0}catch{return}}function yOi(t){return t==="small"?1:t==="medium"?2:4}function bOi(t){try{let n=sNn("/sys/fs/cgroup/cpu.stat","utf-8"),r=Number.parseInt(n.match(/^usage_usec\s+(\d+)/m)?.[1]??"",10);if(Number.isFinite(r))return{usageMicros:r,vcpus:t}}catch{}let e=AOi("/sys/fs/cgroup/cpuacct/cpuacct.usage");if(e!==void 0)return{usageMicros:e/1e3,vcpus:t}}var eEe=class{startedAt=Date.now();profile;vcpus;timer;lastCpu;lastSampleAt;sampleCount=0;observedMs=0;cpuOver95Ms=0;maxCpuPercent=null;stopped;constructor(e){this.profile=e,this.vcpus=yOi(e)}start(){this.sample(),this.timer=setInterval(()=>this.sample(),hOi)}stop(){return this.stopped?this.stopped:(this.timer&&clearInterval(this.timer),this.timer=void 0,this.sample(),this.stopped=this.snapshot(),this.stopped)}snapshot(){return{profile:this.profile,allocatedVcpus:this.vcpus,durationMs:Date.now()-this.startedAt,sampleCount:this.sampleCount,maxCpuPercent:this.maxCpuPercent,cpuOver95DurationMs:this.observedMs>0?this.cpuOver95Ms:null,observedAt:new Date().toISOString()}}sample(){if(this.stopped)return;let e=Date.now(),n=bOi(this.vcpus);if(this.sampleCount+=1,n&&this.lastCpu&&this.lastSampleAt!==void 0){let r=e-this.lastSampleAt,i=Math.max(0,n.usageMicros-this.lastCpu.usageMicros),s=Math.max(0,Math.min(100,i/(r*1e3*n.vcpus)*100));this.observedMs+=Math.max(0,r);let o=Math.max(0,r);s>=gOi&&(this.cpuOver95Ms+=o),this.maxCpuPercent=this.maxCpuPercent===null?s:Math.max(this.maxCpuPercent,s)}n&&(this.lastCpu=n),this.lastSampleAt=e}};var G3;Wxt();var q3="/opt/shipd-agent",mNn="/work/solvers",oNn=4e3,_Oi=["#!/usr/bin/env bash",'if [[ -d "$SHIPD_WORKSPACE_ROOT/src" ]]; then',' export PYTHONPATH="$SHIPD_WORKSPACE_ROOT/src${PYTHONPATH:+:$PYTHONPATH}"',"fi",'exec /usr/local/bin/python "$@"'].join(`
|
|
2366
2366
|
`),xOi="commandcode";function Ku(t){let e=process.env[t];if(!e)throw new Error(`Missing worker environment variable ${t}.`);return e}function SOi(t){return t.length>oNn?t.slice(t.length-oNn):t}function COi(){let t=JSON.parse(bY("/tmp/shipd-bootstrap.json","utf-8")),e=t.mode;if(e!=="solver"&&e!=="patch-precheck")throw new Error("Invalid Fargate bootstrap mode.");process.env.SHIPD_MODE=e;let r=(e==="solver"?["bucket","region","sourceKey","authKey","resultKey"]:["bucket","region","sourceKey","resultKey"]).every(a=>typeof t[a]=="string"&&t[a]),i=["planB64","resourceProfile",...e==="solver"?["provider","modelId","thinkingLevel"]:[]],s={planB64:"SHIPD_PLAN_B64",provider:"SHIPD_PROVIDER",modelId:"SHIPD_MODEL_ID",thinkingLevel:"SHIPD_THINKING_LEVEL",resourceProfile:"SHIPD_RESOURCE_PROFILE"};if(r){let a=[["bucket","SHIPD_S3_BUCKET"],["region","SHIPD_S3_REGION"],["sourceKey","SHIPD_S3_SOURCE_KEY"],...e==="solver"?[["authKey","SHIPD_S3_AUTH_KEY"]]:[],["resultKey","SHIPD_S3_RESULT_KEY"]];for(let[c,u]of a){let l=t[c];if(typeof l!="string"||l.length===0)throw new Error(`Invalid Fargate bootstrap field: ${c}.`);process.env[u]=l}}else{let a=[["sourceUrl","SHIPD_SOURCE_URL"],...e==="solver"?[["authUrl","SHIPD_AUTH_URL"]]:[],["resultPutUrl","SHIPD_RESULT_PUT_URL"],["resultGetUrl","SHIPD_RESULT_GET_URL"]];for(let[c,u]of a){let l=t[c];if(typeof l!="string"||l.length===0)throw new Error(`Invalid Fargate bootstrap field: ${c}.`);process.env[u]=l}}for(let a of i){let c=t[a];if(typeof c!="string"||c.length===0)throw new Error(`Invalid Fargate bootstrap field: ${a}.`);process.env[s[a]]=c}let o=[["timeoutMinutes","SHIPD_TIMEOUT_MINUTES"],...e==="solver"?[["solverCount","SHIPD_SOLVER_COUNT"]]:[]];for(let[a,c]of o){let u=t[a];if(typeof u!="number"||!Number.isFinite(u))throw new Error(`Invalid Fargate bootstrap field: ${a}.`);process.env[c]=String(u)}return e}function TOi(){let t=Ku("SHIPD_PLAN_B64");return JSON.parse(Buffer.from(t,"base64url").toString("utf-8"))}function IOi(t){let e={...process.env};for(let[n,r]of Object.entries(t.env))e[n]=r.replace(/\$\{([A-Za-z_][A-Za-z0-9_]*)\}|\$([A-Za-z_][A-Za-z0-9_]*)/g,(i,s,o)=>e[s??o]??"");return e}async function fNn(t,e){let n=await fetch(t);if(!n.ok)throw new Error(`Download failed (${n.status}) for ${e}.`);W3(e,Buffer.from(await n.arrayBuffer()))}function H3(){return!!process.env.SHIPD_S3_BUCKET}function W9e(){return new tD.S3Client({region:Ku("SHIPD_S3_REGION"),credentials:(0,dNn.defaultProvider)()})}async function hNn(t,e,n){let i=await(await W9e().send(new tD.GetObjectCommand({Bucket:t,Key:e}))).Body?.transformToByteArray();if(!i)throw new Error(`S3 object was empty: s3://${t}/${e}`);W3(n,Buffer.from(i))}async function rEe(t,e){let n=JSON.stringify(e);if(H3()){await W9e().send(new tD.PutObjectCommand({Bucket:Ku("SHIPD_S3_BUCKET"),Key:Ku("SHIPD_S3_RESULT_KEY"),Body:n,ContentType:"application/json"}));return}let r=await fetch(t,{method:"PUT",headers:{"content-type":"application/json"},body:n});if(!r.ok){let i=(await r.text()).trim();throw new Error(`Result upload failed (${r.status})${i?`: ${i.slice(0,1e3)}`:"."}`)}}async function j3(t,e,n,r){return await new Promise((i,s)=>{let o=EOi("/bin/bash",["-c",t],{cwd:e,env:n,stdio:["ignore","pipe","pipe"],detached:process.platform!=="win32"}),a="",c="",u=!1,l,p=d=>{u||(u=!0,l&&clearTimeout(l),i(d))};o.stdout.on("data",d=>{a+=d.toString()}),o.stderr.on("data",d=>{c+=d.toString()}),o.once("error",d=>{u||(u=!0,l&&clearTimeout(l),s(new Error(`${d instanceof Error?d.message:String(d)} (cwd=${e} exists=${iEe(e)})`)))}),o.once("close",d=>p({code:d??1,stdout:a,stderr:c})),l=setTimeout(()=>{process.platform!=="win32"&&o.pid?process.kill(-o.pid,"SIGTERM"):o.kill("SIGTERM"),setTimeout(()=>{u||(process.platform!=="win32"&&o.pid?process.kill(-o.pid,"SIGKILL"):o.kill("SIGKILL"))},5e3)},r)})}async function gR(t,e,n,r){let i=await j3(t,e,n,r);if(i.code!==0)throw new Error([i.stderr.trim(),i.stdout.trim(),`Remote command: ${t}`].filter(Boolean).join(`
|
|
2367
|
-
`));return i}function G9e(t,e){if(!t)return null;let n=t.match(new RegExp(`\\b${e}=["']([0-9]+(?:\\.[0-9]+)?)["']`,"i"))?.[1];if(n===void 0)return null;let r=Number(n);return Number.isFinite(r)?r:null}function aNn(t,e){return t.match(new RegExp(`\\b${e}=["']([^"']*)["']`,"i"))?.[1]}function cNn(t,e){let n=aNn(t,"name")??`testcase-${e+1}`,r=aNn(t,"classname");return r?`${r}::${n}`:n}function wOi(t){try{let e=bY(t,"utf-8"),n=e.match(/<testsuites\b[^>]*>/i)?.[0],r=[...e.matchAll(/<testsuite\b[^>]*>/gi)].map(b=>b[0]),i=[...e.matchAll(/<testcase\b[^>]*(?:\/>|>[\s\S]*?<\/testcase\s*>)/gi)].map(b=>b[0]),s=i.length,o=i.flatMap((b,E)=>/<failure\b/i.test(b)?[cNn(b,E)]:[]),a=i.flatMap((b,E)=>/<error\b/i.test(b)?[cNn(b,E)]:[]),c=o.length,u=a.length,l=i.filter(b=>!/<failure\b/i.test(b)&&!/<error\b/i.test(b)&&!/<skipped\b/i.test(b)).length,p=i.filter(b=>/<skipped\b/i.test(b)).length,d=[...e.matchAll(/<failure\b/gi)].length,m=[...e.matchAll(/<error\b/gi)].length,h=i.reduce((b,E)=>b+[...E.matchAll(/<error\b/gi)].length,0),f=[...e.matchAll(/<skipped\b/gi)].length,g=(b,E)=>{let _=r.map(x=>G9e(x,b)).filter(x=>x!==null);return _.length>0?_.reduce((x,S)=>x+S,0):E},y=(b,E,_)=>Math.max(G9e(n,b)??g(b,_),E);return{tests:G9e(n,"tests")??g("tests",s),testcases:s,failures:y("failures",d,c),failedTestcases:c,passedTestcases:l,errors:y("errors",m,u),erroredTestcases:u,suiteErrors:Math.max(0,m-h),skipped:y("skipped",f,p),skippedTestcases:p,failedTestNames:o,erroredTestNames:a}}catch{return{tests:null,testcases:null,failures:null,failedTestcases:null,passedTestcases:null,errors:null,erroredTestcases:null,suiteErrors:null,skipped:null,skippedTestcases:null,failedTestNames:[],erroredTestNames:[]}}}function vOi(t,e
|
|
2367
|
+
`));return i}function G9e(t,e){if(!t)return null;let n=t.match(new RegExp(`\\b${e}=["']([0-9]+(?:\\.[0-9]+)?)["']`,"i"))?.[1];if(n===void 0)return null;let r=Number(n);return Number.isFinite(r)?r:null}function aNn(t,e){return t.match(new RegExp(`\\b${e}=["']([^"']*)["']`,"i"))?.[1]}function cNn(t,e){let n=aNn(t,"name")??`testcase-${e+1}`,r=aNn(t,"classname");return r?`${r}::${n}`:n}function wOi(t){try{let e=bY(t,"utf-8"),n=e.match(/<testsuites\b[^>]*>/i)?.[0],r=[...e.matchAll(/<testsuite\b[^>]*>/gi)].map(b=>b[0]),i=[...e.matchAll(/<testcase\b[^>]*(?:\/>|>[\s\S]*?<\/testcase\s*>)/gi)].map(b=>b[0]),s=i.length,o=i.flatMap((b,E)=>/<failure\b/i.test(b)?[cNn(b,E)]:[]),a=i.flatMap((b,E)=>/<error\b/i.test(b)?[cNn(b,E)]:[]),c=o.length,u=a.length,l=i.filter(b=>!/<failure\b/i.test(b)&&!/<error\b/i.test(b)&&!/<skipped\b/i.test(b)).length,p=i.filter(b=>/<skipped\b/i.test(b)).length,d=[...e.matchAll(/<failure\b/gi)].length,m=[...e.matchAll(/<error\b/gi)].length,h=i.reduce((b,E)=>b+[...E.matchAll(/<error\b/gi)].length,0),f=[...e.matchAll(/<skipped\b/gi)].length,g=(b,E)=>{let _=r.map(x=>G9e(x,b)).filter(x=>x!==null);return _.length>0?_.reduce((x,S)=>x+S,0):E},y=(b,E,_)=>Math.max(G9e(n,b)??g(b,_),E);return{tests:G9e(n,"tests")??g("tests",s),testcases:s,failures:y("failures",d,c),failedTestcases:c,passedTestcases:l,errors:y("errors",m,u),erroredTestcases:u,suiteErrors:Math.max(0,m-h),skipped:y("skipped",f,p),skippedTestcases:p,failedTestNames:o,erroredTestNames:a}}catch{return{tests:null,testcases:null,failures:null,failedTestcases:null,passedTestcases:null,errors:null,erroredTestcases:null,suiteErrors:null,skipped:null,skippedTestcases:null,failedTestNames:[],erroredTestNames:[]}}}function vOi(t,e){return t.tests===null||t.testcases===null||t.failures===null||t.failedTestcases===null||t.passedTestcases===null||t.errors===null||t.erroredTestcases===null||t.suiteErrors===null||t.skipped===null||t.skippedTestcases===null||t.tests<=0||t.testcases<=0||t.skipped!==0||t.skippedTestcases!==0?!1:e==="all-pass"?t.failures===0&&t.failedTestcases===0&&t.errors===0&&t.erroredTestcases===0&&t.suiteErrors===0:t.passedTestcases===0}async function tEe(t,e,n,r,i,s){let o=`/tmp/shipd-${t}.xml`,a=`./test.sh --output_path ${jr(o)} ${e}`,c={...i,SHIPD_WORKSPACE_ROOT:r,PATH:`${Vl(r,".shipd-bin")}:${i.PATH??""}`};await j3(`rm -f ${jr(o)}`,r,c,3e4);let u=await j3(a,r,c,s),l=wOi(o);return{phase:t,exitCode:u.code,tests:l.tests,testcases:l.testcases,failures:l.failures,failedTestcases:l.failedTestcases,passedTestcases:l.passedTestcases,errors:l.errors,erroredTestcases:l.erroredTestcases,suiteErrors:l.suiteErrors,skipped:l.skipped,skippedTestcases:l.skippedTestcases,failedTestNames:l.failedTestNames,erroredTestNames:l.erroredTestNames,passed:vOi(l,n)}}function ROi(t){switch(t){case"base-before-solution":return"base tests before solution";case"new-before-solution":return"new tests before solution";case"base-after-solution":return"base tests after solution";case"new-after-solution":return"new tests after solution"}}function kOi(t){if(t.phase==="new-before-solution")return t.passedTestcases!==null&&t.passedTestcases>0?"Fix test.patch: add an assertion inside each passing new test for behavior introduced by the solution, so it fails before the solution and passes only after it. Do not remove tests.":t.passedTestcases===0&&t.failedTestcases===0&&t.erroredTestcases===0?"Fix test.patch: each new test must fail or error individually before the solution. Do not rely on a whole-file, module, or import error; move solution-dependent imports or setup into each test (lazy import) so every testcase is collected and reports its own failure or error. Do not remove tests.":"Fix test.patch so every collected new test fails or errors individually before the solution; no new test may pass. Do not remove tests.";switch(t.phase){case"base-before-solution":return"Modify test.sh to exclude the pre solution failing tests, so every base test passes before the solution with no failures or errors.";case"base-after-solution":return"Base-test regression: fix solution.patch so every existing base test still passes after the solution. Do not delete, skip, deselect, weaken, or exclude base tests.";case"new-after-solution":return t.skippedTestcases!==null&&t.skippedTestcases>0?"Fix solution.patch so every new test executes and passes after the solution. Do not skip, xfail, deselect, delete, or weaken new tests.":"Fix solution.patch so every new test passes after the solution with no failures or errors."}}function POi(t){return[`phase: ${ROi(t.phase)}`,`instruction: ${kOi(t)}`,`passed tests: ${t.passedTestcases??"unknown"}`,`skipped tests: ${t.skippedTestcases??"unknown"}`,`failed tests: ${t.failedTestNames.length>0?t.failedTestNames.join(", "):"none"}`,`errored tests: ${t.erroredTestNames.length>0?t.erroredTestNames.join(", "):"none"}`].join(`
|
|
2368
2368
|
`)}async function lNn(t,e,n,r){try{await gR(`git -C ${jr(n)} apply --recount ${jr(t)}`,"/work",r,15*6e4)}catch{throw new Error(`${e} patch could not be applied.`)}}async function uNn(t,e){let n=await Promise.allSettled([t,e]),r=n[0],i=n[1];if(r.status==="rejected")throw r.reason;if(i.status==="rejected")throw i.reason;return[r.value,i.value]}async function pNn(t,e,n){let r=Vl(e,".shipd-bin"),i=Vl(r,"python");await gR(["set -euo pipefail",`rm -rf ${jr(e)}`,`mkdir -p ${jr(e)} ${jr(r)}`,`tar -cf - -C ${jr(t)} . | tar --no-same-owner -xf - -C ${jr(e)}`,`git config --global --add safe.directory ${jr(e)}`,`chmod +x ${jr(Vl(e,"test.sh"))}`,`printf %s ${jr(_Oi)} > ${jr(i)}`,`cp ${jr(i)} ${jr(Vl(r,"python3"))}`,`cp ${jr(i)} ${jr(Vl(r,"python3.12"))}`,`chmod +x ${jr(i)} ${jr(Vl(r,"python3"))} ${jr(Vl(r,"python3.12"))}`].join(`
|
|
2369
2369
|
`),"/work",n,15*6e4)}async function NOi(t,e,n){let r=Date.now(),i=[],s=t.workdir,o=Vl(s,"test.patch"),a="/tmp/shipd-precheck-without-solution",c="/tmp/shipd-precheck-with-solution",u=Math.max(6e4,n*6e4),l=(p,d)=>({platform:"linux",status:"failed",passed:!1,phases:d,durationMs:Date.now()-r,error:POi(p)});try{await lNn(o,"test",s,e),await gR(`chmod +x ${jr(Vl(s,"test.sh"))}`,"/work",e,3e4),await pNn(s,a,e),await pNn(s,c,e),await lNn(Vl(c,"solution.patch"),"solution",c,e);let[p,d]=await uNn(tEe("base-before-solution","base","all-pass",a,e,u),tEe("base-after-solution","base","all-pass",c,e,u));if(i.push(p,d),!p.passed)return l(p,i);if(!d.passed)return l(d,i);let[m,h]=await uNn(tEe("new-before-solution","new","all-fail",a,e,u),tEe("new-after-solution","new","all-pass",c,e,u));return i.length=0,i.push(p,m,d,h),m.passed?h.passed?{platform:"linux",status:"ok",passed:!0,phases:i,durationMs:Date.now()-r}:l(h,i):l(m,i)}catch(p){return{platform:"linux",status:"error",passed:!1,phases:i,durationMs:Date.now()-r,error:p instanceof Error?p.message:String(p)}}finally{await j3(`rm -rf ${jr(a)} ${jr(c)}`,"/work",e,3e4).catch(()=>{})}}function OOi(t){let e=t.trim().replace(/["']/g,"");return/^chmod\s+\S+\s+(?:\S*\/)?test\.sh$/.test(e)}async function DOi(t,e){let n="/tmp/shipd-source.tar.gz";H3()?await hNn(Ku("SHIPD_S3_BUCKET"),Ku("SHIPD_S3_SOURCE_KEY"),n):await fNn(Ku("SHIPD_SOURCE_URL"),n),nEe(t.workdir,{recursive:!0});let r=t.workdir==="/"?":":`find ${jr(t.workdir)} -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +`;await gR(`set -eu
|
|
2370
2370
|
${r}
|