@skyramp/testbot-core 0.13.0-v3.60 → 0.13.0-v3.62

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/dist/post.js CHANGED
@@ -212,7 +212,7 @@ ${o?Et().abandonedBody:Et().cancelledBody}
212
212
  ${s.join(`
213
213
  `)}
214
214
 
215
- <sub>[${Et().workflowRunLinkLabel}](${e})</sub>`}_T();_T();_T();Of();function Fz(t,e){if(process.env.GITHUB_ACTIONS)return new Jg(t,Pye,e);throw new Error("Unrecognized CI environment. Set GITHUB_ACTIONS=true for GitHub Actions, or use EvalPlatform directly for eval/testing.")}dt();var Gw=(r=>(r.Sufficient="sufficient",r.Insufficient="insufficient",r))(Gw||{}),qw=(d=>(d.RelatedChangeConflict="related_change_conflict",d.ReferenceOutsideTarget="reference_outside_target",d.SourceAccessLost="source_access_lost",d.ConfigurationIncomplete="configuration_incomplete",d.CredentialUnavailable="credential_unavailable",d.WorkflowBindingUnverified="workflow_binding_unverified",d.WorkflowRefUnprotected="workflow_ref_unprotected",d.DeploymentReview="deployment_review",d.SutRevisionMismatch="sut_revision_mismatch",d.IsolationUnavailable="isolation_unavailable",d.EvidenceInsufficient="evidence_insufficient",d))(qw||{}),fd=(s=>(s.CiCheck="ci_check",s.TestReport="test_report",s.StaticAnalysis="static_analysis",s.CodeReview="code_review",s.Other="other",s))(fd||{}),QT=(n=>(n.Application="application",n.Environment="environment",n.TestDefect="test_defect",n))(QT||{}),PT=(a=>(a.AuthToken="auth_token",a.Secret="secret",a.DataConnection="data_connection",a.AppNotStarted="app_not_started",a.Login="login",a.ExternalDependency="external_dependency",a.RecordingReplay="recording_replay",a))(PT||{});var DT=(n=>(n.Seed="seed",n.RuntimeMock="runtime_mock",n.EnvOverride="env_override",n))(DT||{}),OT=(n=>(n.TestCode="test_code",n.SkyrampMocker="skyramp_mocker",n.RecipeOverride="recipe_override",n))(OT||{});var Kg=(r=>(r.Ready="ready",r.Blocked="blocked",r))(Kg||{}),Dn=y.string().trim().min(1),Hst=y.object({action:y.enum(["start","run","wait"]),text:Dn,source:y.enum(["file","notes","testbot"]).describe("file: a file in the repository; notes: the Target's notes; testbot: Testbot's own finding"),file:Dn.nullable().describe("the file the step came from, relative to the component's repository; null unless source is file")}).strict().refine(t=>t.source==="file"==(t.file!==null),{path:["file"],message:"a file exactly when the step came from one"}),zst=/^[A-Za-z_][A-Za-z0-9_]*$/,Lz=/^(?!GITHUB_)(?!SKYRAMP_RUNNER_)[A-Z][A-Z0-9_]{0,99}$/,Uz={steps:y.array(Hst).max(20).optional().describe("how it starts, in order"),kind:y.enum(["web_app","service","database","external_service"]).optional(),from:y.object({file:Dn,service:Dn.nullable()}).strict().optional(),credentials:y.array(y.string().max(100).regex(zst)).max(50).optional().describe("the Target's credentials this component's recipe uses: a sign-in (a test user, password or token) or an API key, which the workflow supplies from a GitHub Actions secret as the environment variable of the credential's name; give each name exactly as the recipe reads it. Names only, never a value, and not a secret the recipe generates itself")},jst=y.object({name:Dn,repository:Dn.describe("owner/repo of the repository the component is built from"),healthy:y.boolean(),...Uz}),Vst=y.object({component:Dn,problem:Dn,evidence:Dn.describe("the exact command and output that shows it"),fix:Dn.describe("what the user must provide, and where")}),Wst=y.object({framework:Dn,file:Dn.describe("the test file run, relative to its repository root"),result:y.enum(["pass","fail","error"])}),Yst=y.object({input:Dn.describe("the part of the user's SUT input, as given"),reason:Dn}),$w=y.object({schemaVersion:y.literal(1),status:y.nativeEnum(Kg).describe("ready only if every component came up healthy and the recipe is written"),components:y.array(jst),suites:y.array(Wst).default([]).describe("one run per test suite the Target's repositories have; empty when they have none"),inputsUsed:y.array(Dn).default([]).describe("the parts of the user's SUT input the recipe used"),inputsUsedHow:y.array(y.object({input:Dn.describe("exactly as written in inputsUsed; an entry that is not there is left out"),how:Dn.describe("what the recipe did with it")})).optional(),inputsNotUsed:y.array(Yst).default([]).describe("the parts it did not use, and why"),blockers:y.array(Vst).describe("required (non-empty) when status is blocked")});function Gz(t){if(t.status==="blocked"&&!(Array.isArray(t.blockers)&&t.blockers.length>0))return['status "blocked" requires at least one blocker { component, problem, evidence, fix } \u2014 say what stopped the bring-up'];if(t.status==="ready"){let e=Array.isArray(t.components)?t.components:[];if(e.length===0)return['status "ready" requires at least one component \u2014 name each component the recipe brought up healthy'];let r=e.filter(n=>n?.healthy!==!0).map(n=>String(n?.name??"?"));if(r.length>0)return[`status "ready" requires every component healthy; not healthy: ${r.join(", ")} \u2014 answer "blocked" with a blocker for each, or bring them up and answer "ready" again`]}return[]}var Mf=64*1024,Ff=512*1024,MT=/[\u0000-\u0008\u000b\u000e-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;var Oye=j(require("path"));var Nl=(i=>(i.Clean="clean",i.RecipeDrift="recipe-drift",i.ChangeBrokeBringup="change-broke-bringup",i.BringupFailed="bringup-failed",i))(Nl||{}),hd=y.string().trim().min(1),Jst=y.object({url:y.string().optional().describe("the URL probed, if the check is an HTTP request"),command:y.string().optional().describe("the command run, if the check is a script \u2014 preferred when auth is needed"),headers:y.record(y.string()).optional(),status:y.union([y.number(),y.string()]).optional().describe("expected HTTP status for a url check (default 200)"),output:hd.describe("what the check returned, VERBATIM \u2014 the bytes, nothing appended; this gate RE-RUNS the check")}),Hw=y.object({schemaVersion:y.literal(1),baseUrls:y.record(y.string()).optional().describe("service \u2192 base URL of the running app; omit on a failed bring-up, never invent one"),health:Jst.optional(),teardown:hd.describe("command that tears the SUT down"),dependencies:y.record(y.boolean()).optional().describe("EVERY backing service: true = really deployed, false = mocked; REQUIRED when the app came up"),outcome:y.nativeEnum(Nl).describe("clean | recipe-drift (you corrected the recipe) | change-broke-bringup (the PR broke it) | bringup-failed (environment, not the PR)"),recipeFixes:y.array(y.string()).optional().describe("required when outcome is recipe-drift: what was corrected"),bringupError:y.object({command:hd,output:hd}).optional().describe("required when bring-up failed: the exact failing command and what it printed"),suspectFiles:y.array(y.string()).optional().describe("required for change-broke-bringup: files THIS PR changed that the failure points at, relative to your working directory (a file in another checkout of the run: its absolute path)"),seeded:y.boolean(),startedAt:hd.describe("ISO timestamp"),revisions:y.array(y.object({component:hd.describe("a shared component listed above"),url:hd.describe("the URL on the component's own origin (scheme, host and port) that answers with its deployed commit"),output:hd.describe("what the URL returned, VERBATIM; this gate FETCHES each url")})).optional().describe("only when shared components are listed above: one check per component whose deployed commit you found")});function qz(t){let e=[],r=i=>!!i&&typeof i=="object"&&!Array.isArray(i);return t.outcome!=="change-broke-bringup"&&t.outcome!=="bringup-failed"?(!r(t.baseUrls)||Object.keys(t.baseUrls).length===0?e.push("baseUrls must be a non-empty object of service \u2192 url"):Object.values(t.baseUrls).every(i=>typeof i=="string"&&i.trim())||e.push("every baseUrls value must be a non-empty url string"),r(t.health)?!(typeof t.health.url=="string"&&t.health.url.trim())&&!(typeof t.health.command=="string"&&t.health.command.trim())&&e.push("health must record a url or a command (what you actually ran)"):e.push("health must be an object"),(!r(t.dependencies)||Object.keys(t.dependencies).length===0)&&e.push("dependencies must be a non-empty object of name \u2192 boolean (real vs mocked) when the SUT came up \u2014 account for every backing service so a mocked one can't be hidden behind a green run")):(r(t.bringupError)||e.push(`outcome "${String(t.outcome)}" requires bringupError { command, output } \u2014 the exact failing bring-up command and what it printed`),t.outcome==="change-broke-bringup"&&!(Array.isArray(t.suspectFiles)&&t.suspectFiles.length>0&&t.suspectFiles.every(i=>typeof i=="string"&&i.trim()))&&e.push("change-broke-bringup requires non-empty suspectFiles[] \u2014 the changed file(s) the failure points at; if you cannot point at a file this PR changed, the outcome is bringup-failed (environment/harness), not the PR's fault")),t.outcome==="recipe-drift"&&!(Array.isArray(t.recipeFixes)&&t.recipeFixes.length>0)&&e.push("recipe-drift outcome requires a non-empty recipeFixes[] describing what was corrected"),e}var Vr=y.string().trim().min(1),Mye=(r=>(r.TestbotManaged="testbot_managed",r.CustomerDeployed="customer_deployed",r))(Mye||{}),Fye=(s=>(s.VersionEndpoint="version_endpoint",s.DeployWorkflow="deploy_workflow",s.KubernetesLabels="kubernetes_labels",s.Command="command",s.Api="api",s))(Fye||{}),zw="shared",Kst=[".skyramp/sut/setup.sh",".skyramp/sut/teardown.sh"],Zst=Vr.refine(t=>{try{let e=new URL(t);return["http:","https:"].includes(e.protocol)&&!e.username&&!e.password}catch{return!1}},"an http(s) URL with no login in it"),$z=y.object({address:Zst,readyWhen:Vr.nullable(),credentials:y.array(y.string().regex(Lz)).max(50),revisionSources:y.array(y.object({method:y.nativeEnum(Fye),detail:Vr.describe("what Testbot reads, such as GET /version or the deploy workflow's name"),components:y.array(Vr).min(1).describe("the components it tells the revision of, by name")}).strict()).max(50)}).strict(),Lye=y.object({schemaVersion:y.literal(1),type:y.nativeEnum(Mye).optional(),status:y.enum(["ready","blocked"]),environment:$z.optional(),components:y.array(y.object({name:Vr,repository:Vr.describe(`owner/repo, or "${zw}" for a customer-deployed component no repository builds`),healthy:y.boolean(),url:y.string().min(1).nullable(),...Uz,credentials:y.array(y.string().regex(Lz)).max(50).optional(),deployed:Vr.nullable().optional().describe("customer_deployed only: the revision the component runs, as setup read it (WF-SETUP-22's Deployed now), or null when it could not tell")}).strict()),suites:y.array(y.object({framework:Vr,file:Vr,result:y.enum(["pass","fail","error"])}).strict()),inputsUsed:y.array(Vr),inputsUsedHow:y.array(y.object({input:Vr,how:Vr}).strict()).optional(),inputsNotUsed:y.array(y.object({input:Vr,reason:Vr}).strict()),blockers:y.array(y.object({component:Vr,problem:Vr,evidence:Vr,fix:Vr}).strict()),replay:y.object({outcome:Vr,durationSeconds:y.number().nonnegative().nullable()}).strict().nullable(),files:y.array(y.object({path:Vr,content:y.string()}).strict())}).strict().superRefine((t,e)=>{t.inputsUsedHow?.forEach((i,s)=>{t.inputsUsed.includes(i.input)||e.addIssue({code:y.ZodIssueCode.custom,path:["inputsUsedHow",s,"input"],message:"not one of inputsUsed"})}),t.components.forEach((i,s)=>{i.credentials&&new Set(i.credentials).size!==i.credentials.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"credentials"],message:"names a credential twice"})});let r=t.type==="customer_deployed";r!==(t.environment!==void 0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["environment"],message:"an environment exactly when the plan's type is customer_deployed"}),t.environment&&new Set(t.environment.credentials).size!==t.environment.credentials.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["environment","credentials"],message:"names a credential twice"});let n=new Set(t.components.map(i=>i.name));if(t.environment?.revisionSources.forEach((i,s)=>{i.components.forEach((o,a)=>{n.has(o)||e.addIssue({code:y.ZodIssueCode.custom,path:["environment","revisionSources",s,"components",a],message:"not one of components"})})}),t.components.forEach((i,s)=>{i.deployed!==void 0!==r&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"deployed"],message:"what a component runs exactly when the plan is customer_deployed"}),i.repository===zw&&!r&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"repository"],message:`only a customer_deployed component is ${zw}`})}),r&&(t.components.forEach((i,s)=>{t.components.findIndex(o=>o.name===i.name)!==s&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"name"],message:"names a component twice"})}),t.replay!==null&&e.addIssue({code:y.ZodIssueCode.custom,path:["replay"],message:"a customer_deployed plan is never replayed"}),t.files.forEach((i,s)=>{Kst.includes(i.path)&&e.addIssue({code:y.ZodIssueCode.custom,path:["files",s,"path"],message:"a customer_deployed plan has no script that starts or stops the system"})})),r&&t.status==="ready"){let i=new Set(t.environment?.revisionSources.flatMap(s=>s.components));t.components.forEach((s,o)=>{s.repository!==zw&&!i.has(s.name)&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",o,"name"],message:"a ready customer_deployed plan reads what every component a repository builds runs"})})}});var WHt=["clean","recipe-drift"];function FT(t){let e=Oye.posix.normalize(t);return e===t&&(e===".skyramp/workspace.yml"||e.startsWith(".skyramp/sut/")&&e.length>13)}var Uye=[".skyramp/sut/setup.sh",".skyramp/sut/teardown.sh",".skyramp/sut/health.json",".skyramp/workspace.yml"];var UT=(e=>(e.GitHub="github",e))(UT||{}),jz=(s=>(s.Trigger="TRIGGER",s.Baseline="BASELINE",s.AuthoritativeRelatedChange="AUTHORITATIVE_RELATED_CHANGE",s.ConfiguredSharedDependency="CONFIGURED_SHARED_DEPENDENCY",s.ManualOverride="MANUAL_OVERRIDE",s))(jz||{}),GT=(s=>(s.PullRequest="pull_request",s.ReadyForReview="ready_for_review",s.Schedule="schedule",s.Manual="manual",s.SutSetup="sut_setup",s))(GT||{}),Xst=["sut_setup"],eot=["sut_setup","schedule"];var qT=(e=>(e.PullRequest="pull_request",e))(qT||{}),$T=(i=>(i.ModelProvider="model_provider",i.SutAccess="sut_access",i.Skyramp="skyramp",i.Other="other",i))($T||{});var HT=(i=>(i.ReportUploaded="report_uploaded",i.NoReport="no_report",i.Cancelled="cancelled",i.RecipeUploaded="recipe_uploaded",i))(HT||{}),Vye=(r=>(r.Running="running",r.Ended="ended",r))(Vye||{}),zT=(o=>(o.TimedOut="timed_out",o.AgentFailed="agent_failed",o.SetupFailed="setup_failed",o.CredentialMissing="credential_missing",o.RunnerLost="runner_lost",o.CheckoutFailed="checkout_failed",o))(zT||{}),Vz=(r=>(r.Onboard="onboard",r.Deploy="deploy",r))(Vz||{}),Wz=(r=>(r.Started="started",r.Finished="finished",r))(Wz||{}),Yz=(n=>(n.Passed="passed",n.Failed="failed",n.Skipped="skipped",n))(Yz||{});var jT=(n=>(n.ChooseCredential="choose_credential",n.FreeText="free_text",n.ChooseSource="choose_source",n))(jT||{});var VT=(n=>(n.Discover="discover",n.Start="start",n.Check="check",n))(VT||{}),WT=(i=>(i.Found="found",i.Starting="starting",i.Up="up",i.OutOfScope="out_of_scope",i))(WT||{}),Ql=y.string().regex(/^[1-9][0-9]{0,18}$/),eA=y.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/),_l=y.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/),Xg=y.string().regex(/^[0-9a-f]{64}$/),LT=y.object({id:_l,revision:Ql}).strict(),As=y.object({provider:y.nativeEnum(UT),providerAccountId:Ql,providerRepositoryId:Ql}).strict(),zz=t=>`${t.provider}:${t.providerRepositoryId}`,Wye=As.extend({namespace:y.string().min(1),name:y.string().min(1)}).strict(),Gye=y.object({ref:y.string().min(1),revision:eA}).strict(),tot=y.object({repository:Wye,revision:eA,ref:y.string().nullable(),reason:y.nativeEnum(jz),change:y.object({kind:y.nativeEnum(qT),number:Ql,base:Gye.optional()}).strict().nullable(),compareWith:Gye.optional()}).strict();function rot(t){let e=t.baseline.sources,r=t.trigger.kind==="schedule"&&t.baseline.confirmedRunId!==null;if(!e)return r?"names every source of the snapshot, once":null;if(t.baseline.confirmedRunId===null)return"a run with no Confirmed Baseline names no baseline sources";let n=new Set(t.sources.map(o=>zz(o.repository))),i=new Set(e.map(o=>zz(o.repository)));return i.size===e.length&&i.size===n.size&&[...i].every(o=>n.has(o))?null:"names every source of the snapshot, once"}var not=y.object({schemaVersion:y.literal(1),runId:_l,skyrampOrgId:_l,target:LT,policy:LT.nullable(),trigger:y.object({kind:y.nativeEnum(GT),requestedAt:y.string().datetime()}).strict(),configuration:y.object({captureId:_l,revision:Ql.nullable(),hash:Xg}).strict(),baseline:y.object({confirmedRunId:_l.nullable(),sources:y.array(y.object({repository:As,revision:eA}).strict()).optional()}).strict(),sources:y.array(tot).min(1),execution:y.object({provider:y.nativeEnum(UT),repository:Wye,workflowPath:y.string(),ref:y.string()}).strict(),credentials:y.array(y.object({name:y.string(),purpose:y.nativeEnum($T),required:y.boolean()}).strict())}).strict().superRefine((t,e)=>{let r=t.trigger.kind,n=Xst.includes(r);n!==(t.policy===null)&&e.addIssue({code:y.ZodIssueCode.custom,path:["policy"],message:n?`a ${r} run has no policy`:"names the policy revision"});let i=rot(t);i&&e.addIssue({code:y.ZodIssueCode.custom,path:["baseline","sources"],message:i}),eot.includes(r)&&t.sources.forEach((s,o)=>{s.reason!=="BASELINE"&&e.addIssue({code:y.ZodIssueCode.custom,path:["sources",o,"reason"],message:`a ${r} run selects only BASELINE sources`})}),t.sources.forEach((s,o)=>{s.compareWith&&(s.reason!=="MANUAL_OVERRIDE"||s.change)&&e.addIssue({code:y.ZodIssueCode.custom,path:["sources",o,"compareWith"],message:"only a MANUAL_OVERRIDE source without a change names a revision to compare with"})})});var Jz=(r=>(r.Person="person",r.Policy="policy",r))(Jz||{}),iot=y.object({kind:y.nativeEnum(Jz),name:y.string().min(1).max(256)}).strict(),ezt=y.object({schemaVersion:y.literal(1),attempt:y.object({id:_l,number:y.number().int().positive(),deadlineAt:y.string().datetime()}).strict(),manifest:not,manifestHash:Xg,entitlements:y.array(y.string()).optional(),runToken:y.string().min(1).optional(),requestedBy:iot.nullable().optional()}).strict(),tzt=y.object({schemaVersion:y.literal(1),runId:_l,execution:y.object({provider:y.nativeEnum(UT),repository:As,providerRunId:Ql,providerRunAttempt:y.number().int().positive(),workflowRef:y.string().min(1)}).strict(),requester:y.literal(!0).optional()}).strict(),qye=y.object({repository:As,number:Ql}).strict(),sot=y.discriminatedUnion("status",[y.object({status:y.literal("none")}).strict(),y.object({status:y.literal("delivered"),pullRequests:y.array(qye).min(1)}).strict(),y.object({status:y.literal("partial"),pullRequests:y.array(qye).min(1),failures:y.array(y.object({repository:As,message:y.string()}).strict()).min(1)}).strict(),y.object({status:y.literal("failed"),message:y.string()}).strict()]),oot=y.object({sufficiency:y.nativeEnum(Gw),rationale:y.string().min(1),gaps:y.array(y.string().min(1)),acceptedEvidence:y.array(y.object({kind:y.nativeEnum(fd),repository:y.string().min(1),revision:eA,reference:y.string().min(1),summary:y.string().min(1)}).strict()),actionRequired:y.array(y.nativeEnum(qw))}).strict().superRefine((t,e)=>{t.sufficiency==="sufficient"!=(t.gaps.length===0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["gaps"],message:"sufficient evidence has no gaps, and insufficient evidence names at least one"}),new Set(t.actionRequired).size!==t.actionRequired.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["actionRequired"],message:"a reason appears twice"})}),Zg=500,Kz=2048,Zz=1e3,aot=y.object({testResults:y.array(y.object({resultId:y.string().regex(/^[^\u0000-\u001f]{1,512}$/),repository:y.string().regex(/^[A-Za-z0-9._-]{1,100}\/[A-Za-z0-9._-]{1,100}$/),testType:y.string(),endpoint:y.string(),status:y.enum(["Pass","Fail","Skipped"]),details:y.string(),fileName:y.string().optional(),testId:y.string().regex(/^[^\u0000-\u001f]{1,512}$/).optional(),failureCause:y.object({kind:y.nativeEnum(QT),family:y.nativeEnum(PT).optional()}).strict().optional()}).strict()),validation:oot,changes:y.array(y.object({id:y.string().min(1).max(512),text:y.string().max(16384),source:y.string().max(Kz),testedBy:y.array(y.string().min(1).max(1024)).max(Zg),writtenBy:y.array(y.string().min(1).max(1024)).max(Zg),maintainedBy:y.array(y.string().min(1).max(1024)).max(Zg)}).strict()).max(Zz).optional(),traceVersion:y.literal(1)}).passthrough().superRefine((t,e)=>{let r=new Set;t.testResults.forEach((n,i)=>{let s=n.failureCause;s&&n.status==="Pass"&&e.addIssue({code:y.ZodIssueCode.custom,path:["testResults",i,"failureCause"],message:"a passing result has no failure cause"}),s&&s.kind==="environment"!=(s.family!==void 0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["testResults",i,"failureCause"],message:"an environment cause names its family, and no other cause does"});let o=["testResults",i,"resultId"],a=n.resultId.slice(`${n.repository}:${n.testType}:`.length);(!n.resultId.startsWith(`${n.repository}:${n.testType}:`)||a===""||a.startsWith("/")||a.startsWith("#"))&&e.addIssue({code:y.ZodIssueCode.custom,path:o,message:"is not repository:testType:path for this result"}),r.has(n.resultId)&&e.addIssue({code:y.ZodIssueCode.custom,path:o,message:"stable identity repeated"}),r.add(n.resultId)})}),$ye=t=>t.replace(/[%:#]/g,e=>encodeURIComponent(e));function Xz(t,e,r,n){let i=r.replace(/\\/g,"/"),s=i.split("/").filter(c=>c!==""&&c!==".");if(i.startsWith("/")||/^[A-Za-z]:/.test(i)||s.length===0||s.includes(".."))throw new Error(`testResultId needs a path relative to the repository root, got "${r}"`);let o=s.join("/"),a=`${t}:${e}:${$ye(o)}`;return n?`${a}#${$ye(n)}`:a}var Yye=50,Hye=y.string().regex(/^(?!.*\.\.)(?!\/)(?!.*\/$)[A-Za-z0-9._/-]{1,255}$/),Jye=y.discriminatedUnion("kind",[y.object({kind:y.literal("branch_pushed"),repository:As,branch:Hye,commit:eA}).strict(),y.object({kind:y.enum(["pull_request_opened","pull_request_edited"]),repository:As,number:Ql,branch:Hye}).strict()]),cot=y.object({schemaVersion:y.literal(1),run:y.object({runId:_l,attemptNumber:y.number().int().positive(),manifestHash:Xg}).strict(),target:LT,policy:LT,provenance:y.object({sources:y.array(y.object({repository:As,revision:eA}).strict())}).strict(),delivery:y.object({generatedTests:sot}).strict(),producer:y.object({testbot:y.string().regex(/^[0-9A-Za-z.+-]{1,64}$/),mcp:y.string().regex(/^[0-9A-Za-z.+-]{1,64}$/)}).strict(),report:aot,githubWrites:y.array(Jye).max(Yye).optional()}).strict(),rzt=cot.pick({schemaVersion:!0,run:!0,target:!0,provenance:!0,producer:!0}).extend({recipe:Lye}).strict().superRefine(({recipe:t},e)=>{let r=(s,o)=>e.addIssue({code:y.ZodIssueCode.custom,path:["recipe",...s],message:o});t.status==="blocked"&&t.blockers.length===0&&r(["blockers"],"a blocked recipe names at least one blocker");let n=new Set,i=0;t.files.forEach((s,o)=>{FT(s.path)||r(["files",o,"path"],"is not .skyramp/workspace.yml or a file under .skyramp/sut/"),n.has(s.path)&&r(["files",o,"path"],"file repeated"),n.add(s.path),MT.test(s.content)&&r(["files",o,"content"],"is not text");let a=Buffer.byteLength(s.content,"utf8");i+=a,a>Mf&&r(["files",o,"content"],`larger than ${Mf} bytes`)}),i>Ff&&r(["files"],`recipe text over ${Ff} bytes in total`)}),lot=y.object({schemaVersion:y.literal(1),stage:y.nativeEnum(Vz),event:y.nativeEnum(Wz),status:y.nativeEnum(Yz).optional()}).strict().refine(t=>t.status===void 0||t.event==="finished",{path:["status"],message:"only a finished stage has a status"}),Vw=100,Kye=5e3,Hz=2e3,zye=100,Zye=/^[A-Za-z0-9_-]{1,64}$/,uot=20,dot=200,Xye=100,pot=100,fot=200,hot=500,ej=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/,mc=t=>y.string().max(t).refine(e=>e.trim().length>0&&!ej.test(e),"blank, or not text"),jw={sequence:y.number().int().min(1).max(Kye),at:y.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/)},eEe=y.discriminatedUnion("kind",[y.object({...jw,kind:y.literal("log"),phase:y.nativeEnum(VT),text:mc(Hz)}).strict(),y.object({...jw,kind:y.literal("component"),name:mc(zye),repository:mc(fot).refine(t=>/^[^/\s]+\/[^/\s]+$/.test(t),"not owner/name"),file:mc(hot).nullable(),status:y.nativeEnum(WT),version:mc(pot).optional()}).strict(),y.object({...jw,kind:y.literal("reply"),to:y.string().uuid(),accepted:y.boolean(),text:mc(Hz)}).strict(),y.object({...jw,kind:y.literal("question"),id:y.string().regex(Zye),type:y.nativeEnum(jT),text:mc(Hz),component:mc(zye).nullable(),choices:y.array(mc(dot)).max(uot),placeholder:mc(Xye).optional()}).strict(),y.object({...jw,kind:y.literal("delivered"),ids:y.array(y.string().uuid()).max(Vw)}).strict()]).superRefine((t,e)=>{t.kind==="question"&&(t.type==="free_text"!=(t.choices.length===0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["choices"],message:"choices exactly when the question is not free_text"}),new Set(t.choices).size!==t.choices.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["choices"],message:"a choice twice"}))}),mot=y.object({schemaVersion:y.literal(1),events:y.array(eEe).max(Vw)}).strict(),jye={id:y.string().uuid(),text:y.string(),by:y.string().nullable(),at:y.string()},got=y.discriminatedUnion("kind",[y.object({...jye,kind:y.literal("instruction"),question:y.null()}),y.object({...jye,kind:y.literal("answer"),question:y.string().regex(Zye)})]),Aot=y.object({schemaVersion:y.literal(1),leftOut:y.array(y.string()),messages:y.array(got).optional()}),yot=y.object({schemaVersion:y.literal(1),configuration:y.object({captureId:_l,revision:Ql.nullable(),hash:Xg}).strict(),capture:y.object({schemaVersion:y.literal(1),settings:y.object({target:y.object({environment:y.unknown()}).passthrough(),timeoutMinutes:y.number().int().positive()}).passthrough(),provenance:y.object({}).passthrough(),hash:Xg}).strict()}).strict(),Eot=$z.extend({version:y.number().int().positive(),components:y.array(y.object({name:y.string().min(1),repository:y.string().min(1)}).strict())}).strict(),bot=y.object({schemaVersion:y.literal(1),testEnvironment:Eot.nullable().optional(),runtimePlan:y.object({version:y.number().int().positive(),contentHash:Xg,files:y.array(y.object({path:y.string().refine(FT,"not a recipe file path"),content:y.string()}).strict()).min(1)}).strict().nullable()}).strict().refine(t=>!(t.runtimePlan&&t.testEnvironment),{message:"a run pins a recipe or a customer's Test Environment, never both",path:["testEnvironment"]}),wot=y.object({schemaVersion:y.literal(1),state:y.nativeEnum(Vye),ending:y.nativeEnum(HT).nullable(),cause:y.nativeEnum(zT).nullable(),deadlineAt:y.string().datetime()}).strict();function tEe(t){return t.reportUploaded?{schemaVersion:1,reason:"report_uploaded",cause:null}:t.recipeUploaded?{schemaVersion:1,reason:"recipe_uploaded",cause:null}:t.cancelled?{schemaVersion:1,reason:"cancelled",cause:null}:{schemaVersion:1,reason:"no_report",cause:Cot(t)}}function Cot(t){return t.credentialMissing?"credential_missing":t.checkoutFailed?"checkout_failed":t.setupFailed?"setup_failed":t.timedOut?"timed_out":"agent_failed"}var Iot=["generate-unit","native-run","finalize"],Sot=["preflight","plan","write",...Iot,"report","deliver"];var tA=(n=>(n.Unit="unit",n.Integration="integration",n.E2e="e2e",n))(tA||{});var YT=1440;dt();Pz();Of();var rEe;(function(t){t[t.lineFeed=10]="lineFeed",t[t.carriageReturn=13]="carriageReturn",t[t.space=32]="space",t[t._0=48]="_0",t[t._1=49]="_1",t[t._2=50]="_2",t[t._3=51]="_3",t[t._4=52]="_4",t[t._5=53]="_5",t[t._6=54]="_6",t[t._7=55]="_7",t[t._8=56]="_8",t[t._9=57]="_9",t[t.a=97]="a",t[t.b=98]="b",t[t.c=99]="c",t[t.d=100]="d",t[t.e=101]="e",t[t.f=102]="f",t[t.g=103]="g",t[t.h=104]="h",t[t.i=105]="i",t[t.j=106]="j",t[t.k=107]="k",t[t.l=108]="l",t[t.m=109]="m",t[t.n=110]="n",t[t.o=111]="o",t[t.p=112]="p",t[t.q=113]="q",t[t.r=114]="r",t[t.s=115]="s",t[t.t=116]="t",t[t.u=117]="u",t[t.v=118]="v",t[t.w=119]="w",t[t.x=120]="x",t[t.y=121]="y",t[t.z=122]="z",t[t.A=65]="A",t[t.B=66]="B",t[t.C=67]="C",t[t.D=68]="D",t[t.E=69]="E",t[t.F=70]="F",t[t.G=71]="G",t[t.H=72]="H",t[t.I=73]="I",t[t.J=74]="J",t[t.K=75]="K",t[t.L=76]="L",t[t.M=77]="M",t[t.N=78]="N",t[t.O=79]="O",t[t.P=80]="P",t[t.Q=81]="Q",t[t.R=82]="R",t[t.S=83]="S",t[t.T=84]="T",t[t.U=85]="U",t[t.V=86]="V",t[t.W=87]="W",t[t.X=88]="X",t[t.Y=89]="Y",t[t.Z=90]="Z",t[t.asterisk=42]="asterisk",t[t.backslash=92]="backslash",t[t.closeBrace=125]="closeBrace",t[t.closeBracket=93]="closeBracket",t[t.colon=58]="colon",t[t.comma=44]="comma",t[t.dot=46]="dot",t[t.doubleQuote=34]="doubleQuote",t[t.minus=45]="minus",t[t.openBrace=123]="openBrace",t[t.openBracket=91]="openBracket",t[t.plus=43]="plus",t[t.slash=47]="slash",t[t.formFeed=12]="formFeed",t[t.tab=9]="tab"})(rEe||(rEe={}));var vot=new Array(20).fill(0).map((t,e)=>" ".repeat(e)),rA=200,Bot={" ":{"\n":new Array(rA).fill(0).map((t,e)=>`
215
+ <sub>[${Et().workflowRunLinkLabel}](${e})</sub>`}_T();_T();_T();Of();function Fz(t,e){if(process.env.GITHUB_ACTIONS)return new Jg(t,Pye,e);throw new Error("Unrecognized CI environment. Set GITHUB_ACTIONS=true for GitHub Actions, or use EvalPlatform directly for eval/testing.")}dt();var Gw=(r=>(r.Sufficient="sufficient",r.Insufficient="insufficient",r))(Gw||{}),qw=(d=>(d.RelatedChangeConflict="related_change_conflict",d.ReferenceOutsideTarget="reference_outside_target",d.SourceAccessLost="source_access_lost",d.ConfigurationIncomplete="configuration_incomplete",d.CredentialUnavailable="credential_unavailable",d.WorkflowBindingUnverified="workflow_binding_unverified",d.WorkflowRefUnprotected="workflow_ref_unprotected",d.DeploymentReview="deployment_review",d.SutRevisionMismatch="sut_revision_mismatch",d.IsolationUnavailable="isolation_unavailable",d.EvidenceInsufficient="evidence_insufficient",d))(qw||{}),fd=(s=>(s.CiCheck="ci_check",s.TestReport="test_report",s.StaticAnalysis="static_analysis",s.CodeReview="code_review",s.Other="other",s))(fd||{}),QT=(n=>(n.Application="application",n.Environment="environment",n.TestDefect="test_defect",n))(QT||{}),PT=(a=>(a.AuthToken="auth_token",a.Secret="secret",a.DataConnection="data_connection",a.AppNotStarted="app_not_started",a.Login="login",a.ExternalDependency="external_dependency",a.RecordingReplay="recording_replay",a))(PT||{});var DT=(n=>(n.Seed="seed",n.RuntimeMock="runtime_mock",n.EnvOverride="env_override",n))(DT||{}),OT=(n=>(n.TestCode="test_code",n.SkyrampMocker="skyramp_mocker",n.RecipeOverride="recipe_override",n))(OT||{});var Kg=(r=>(r.Ready="ready",r.Blocked="blocked",r))(Kg||{}),Dn=y.string().trim().min(1),Hst=y.object({action:y.enum(["start","run","wait"]),text:Dn,source:y.enum(["file","notes","testbot"]).describe("file: a file in the repository; notes: the Target's notes; testbot: Testbot's own finding"),file:Dn.nullable().describe("the file the step came from, relative to the component's repository; null unless source is file")}).strict().refine(t=>t.source==="file"==(t.file!==null),{path:["file"],message:"a file exactly when the step came from one"}),zst=/^[A-Za-z_][A-Za-z0-9_]*$/,Lz=/^(?!GITHUB_)(?!SKYRAMP_RUNNER_)[A-Z][A-Z0-9_]{0,99}$/,Uz={steps:y.array(Hst).max(20).optional().describe("how it starts, in order"),kind:y.enum(["web_app","service","database","external_service"]).optional(),from:y.object({file:Dn,service:Dn.nullable()}).strict().optional(),credentials:y.array(y.string().max(100).regex(zst)).max(50).optional().describe("the Target's credentials this component's recipe uses: a sign-in (a test user, password or token) or an API key, which the workflow supplies from a GitHub Actions secret as the environment variable of the credential's name; give each name exactly as the recipe reads it. Names only, never a value, and not a secret the recipe generates itself")},jst=y.object({name:Dn,repository:Dn.describe("owner/repo of the repository the component is built from"),healthy:y.boolean(),...Uz}),Vst=y.object({component:Dn,problem:Dn,evidence:Dn.describe("the exact command and output that shows it"),fix:Dn.describe("what the user must provide, and where")}),Wst=y.object({framework:Dn,file:Dn.describe("the test file run, relative to its repository root"),result:y.enum(["pass","fail","error"])}),Yst=y.object({input:Dn.describe("the part of the user's SUT input, as given"),reason:Dn}),$w=y.object({schemaVersion:y.literal(1),status:y.nativeEnum(Kg).describe("ready only if every component came up healthy and the recipe is written"),components:y.array(jst),suites:y.array(Wst).default([]).describe("one run per test suite the Target's repositories have; empty when they have none"),inputsUsed:y.array(Dn).default([]).describe("the parts of the user's SUT input the recipe used"),inputsUsedHow:y.array(y.object({input:Dn.describe("exactly as written in inputsUsed; an entry that is not there is left out"),how:Dn.describe("what the recipe did with it")})).optional(),inputsNotUsed:y.array(Yst).default([]).describe("the parts it did not use, and why"),blockers:y.array(Vst).describe("required (non-empty) when status is blocked")});function Gz(t){if(t.status==="blocked"&&!(Array.isArray(t.blockers)&&t.blockers.length>0))return['status "blocked" requires at least one blocker { component, problem, evidence, fix } \u2014 say what stopped the bring-up'];if(t.status==="ready"){let e=Array.isArray(t.components)?t.components:[];if(e.length===0)return['status "ready" requires at least one component \u2014 name each component the recipe brought up healthy'];let r=e.filter(n=>n?.healthy!==!0).map(n=>String(n?.name??"?"));if(r.length>0)return[`status "ready" requires every component healthy; not healthy: ${r.join(", ")} \u2014 answer "blocked" with a blocker for each, or bring them up and answer "ready" again`]}return[]}var Mf=64*1024,Ff=512*1024,MT=/[\u0000-\u0008\u000b\u000e-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/;var Oye=j(require("path"));var Nl=(i=>(i.Clean="clean",i.RecipeDrift="recipe-drift",i.ChangeBrokeBringup="change-broke-bringup",i.BringupFailed="bringup-failed",i))(Nl||{}),hd=y.string().trim().min(1),Jst=y.object({url:y.string().optional().describe("the URL probed, if the check is an HTTP request"),command:y.string().optional().describe("the command run, if the check is a script \u2014 preferred when auth is needed"),headers:y.record(y.string()).optional(),status:y.union([y.number(),y.string()]).optional().describe("expected HTTP status for a url check (default 200)"),output:hd.describe("what the check returned, VERBATIM \u2014 the bytes, nothing appended; this gate RE-RUNS the check")}),Hw=y.object({schemaVersion:y.literal(1),baseUrls:y.record(y.string()).optional().describe("service \u2192 base URL of the running app; omit on a failed bring-up, never invent one"),health:Jst.optional(),teardown:hd.describe("command that tears the SUT down"),dependencies:y.record(y.boolean()).optional().describe("EVERY backing service: true = really deployed, false = mocked; REQUIRED when the app came up"),outcome:y.nativeEnum(Nl).describe("clean | recipe-drift (you corrected the recipe) | change-broke-bringup (the PR broke it) | bringup-failed (environment, not the PR)"),recipeFixes:y.array(y.string()).optional().describe("required when outcome is recipe-drift: what was corrected"),bringupError:y.object({command:hd,output:hd}).optional().describe("required when bring-up failed: the exact failing command and what it printed"),suspectFiles:y.array(y.string()).optional().describe("required for change-broke-bringup: files THIS PR changed that the failure points at, relative to your working directory (a file in another checkout of the run: its absolute path)"),seeded:y.boolean(),startedAt:hd.describe("ISO timestamp"),revisions:y.array(y.object({component:hd.describe("a shared component listed above"),url:hd.describe("the URL on the component's own origin (scheme, host and port) that answers with its deployed commit"),output:hd.describe("what the URL returned, VERBATIM; this gate FETCHES each url")})).optional().describe("only when shared components are listed above: one check per component whose deployed commit you found")});function qz(t){let e=[],r=i=>!!i&&typeof i=="object"&&!Array.isArray(i);return t.outcome!=="change-broke-bringup"&&t.outcome!=="bringup-failed"?(!r(t.baseUrls)||Object.keys(t.baseUrls).length===0?e.push("baseUrls must be a non-empty object of service \u2192 url"):Object.values(t.baseUrls).every(i=>typeof i=="string"&&i.trim())||e.push("every baseUrls value must be a non-empty url string"),r(t.health)?!(typeof t.health.url=="string"&&t.health.url.trim())&&!(typeof t.health.command=="string"&&t.health.command.trim())&&e.push("health must record a url or a command (what you actually ran)"):e.push("health must be an object"),(!r(t.dependencies)||Object.keys(t.dependencies).length===0)&&e.push("dependencies must be a non-empty object of name \u2192 boolean (real vs mocked) when the SUT came up \u2014 account for every backing service so a mocked one can't be hidden behind a green run")):(r(t.bringupError)||e.push(`outcome "${String(t.outcome)}" requires bringupError { command, output } \u2014 the exact failing bring-up command and what it printed`),t.outcome==="change-broke-bringup"&&!(Array.isArray(t.suspectFiles)&&t.suspectFiles.length>0&&t.suspectFiles.every(i=>typeof i=="string"&&i.trim()))&&e.push("change-broke-bringup requires non-empty suspectFiles[] \u2014 the changed file(s) the failure points at; if you cannot point at a file this PR changed, the outcome is bringup-failed (environment/harness), not the PR's fault")),t.outcome==="recipe-drift"&&!(Array.isArray(t.recipeFixes)&&t.recipeFixes.length>0)&&e.push("recipe-drift outcome requires a non-empty recipeFixes[] describing what was corrected"),e}var Vr=y.string().trim().min(1),Mye=(r=>(r.TestbotManaged="testbot_managed",r.CustomerDeployed="customer_deployed",r))(Mye||{}),Fye=(s=>(s.VersionEndpoint="version_endpoint",s.DeployWorkflow="deploy_workflow",s.KubernetesLabels="kubernetes_labels",s.Command="command",s.Api="api",s))(Fye||{}),zw="shared",Kst=[".skyramp/sut/setup.sh",".skyramp/sut/teardown.sh"],Zst=Vr.refine(t=>{try{let e=new URL(t);return["http:","https:"].includes(e.protocol)&&!e.username&&!e.password}catch{return!1}},"an http(s) URL with no login in it"),$z=y.object({address:Zst,readyWhen:Vr.nullable(),credentials:y.array(y.string().regex(Lz)).max(50),revisionSources:y.array(y.object({method:y.nativeEnum(Fye),detail:Vr.describe("what Testbot reads, such as GET /version or the deploy workflow's name"),components:y.array(Vr).min(1).describe("the components it tells the revision of, by name")}).strict()).max(50)}).strict(),Lye=y.object({schemaVersion:y.literal(1),type:y.nativeEnum(Mye).optional(),status:y.enum(["ready","blocked"]),environment:$z.optional(),components:y.array(y.object({name:Vr,repository:Vr.describe(`owner/repo, or "${zw}" for a customer-deployed component no repository builds`),healthy:y.boolean(),url:y.string().min(1).nullable(),...Uz,credentials:y.array(y.string().regex(Lz)).max(50).optional(),deployed:Vr.nullable().optional().describe("customer_deployed only: the revision the component runs, as setup read it (WF-SETUP-22's Deployed now), or null when it could not tell")}).strict()),suites:y.array(y.object({framework:Vr,file:Vr,result:y.enum(["pass","fail","error"])}).strict()),inputsUsed:y.array(Vr),inputsUsedHow:y.array(y.object({input:Vr,how:Vr}).strict()).optional(),inputsNotUsed:y.array(y.object({input:Vr,reason:Vr}).strict()),blockers:y.array(y.object({component:Vr,problem:Vr,evidence:Vr,fix:Vr}).strict()),replay:y.object({outcome:Vr,durationSeconds:y.number().nonnegative().nullable()}).strict().nullable(),files:y.array(y.object({path:Vr,content:y.string()}).strict())}).strict().superRefine((t,e)=>{t.inputsUsedHow?.forEach((i,s)=>{t.inputsUsed.includes(i.input)||e.addIssue({code:y.ZodIssueCode.custom,path:["inputsUsedHow",s,"input"],message:"not one of inputsUsed"})}),t.components.forEach((i,s)=>{i.credentials&&new Set(i.credentials).size!==i.credentials.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"credentials"],message:"names a credential twice"})});let r=t.type==="customer_deployed";r!==(t.environment!==void 0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["environment"],message:"an environment exactly when the plan's type is customer_deployed"}),t.environment&&new Set(t.environment.credentials).size!==t.environment.credentials.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["environment","credentials"],message:"names a credential twice"});let n=new Set(t.components.map(i=>i.name));if(t.environment?.revisionSources.forEach((i,s)=>{i.components.forEach((o,a)=>{n.has(o)||e.addIssue({code:y.ZodIssueCode.custom,path:["environment","revisionSources",s,"components",a],message:"not one of components"})})}),t.components.forEach((i,s)=>{i.deployed!==void 0!==r&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"deployed"],message:"what a component runs exactly when the plan is customer_deployed"}),i.repository===zw&&!r&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"repository"],message:`only a customer_deployed component is ${zw}`})}),r&&(t.components.forEach((i,s)=>{t.components.findIndex(o=>o.name===i.name)!==s&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",s,"name"],message:"names a component twice"})}),t.replay!==null&&e.addIssue({code:y.ZodIssueCode.custom,path:["replay"],message:"a customer_deployed plan is never replayed"}),t.files.forEach((i,s)=>{Kst.includes(i.path)&&e.addIssue({code:y.ZodIssueCode.custom,path:["files",s,"path"],message:"a customer_deployed plan has no script that starts or stops the system"})})),r&&t.status==="ready"){let i=new Set(t.environment?.revisionSources.flatMap(s=>s.components));t.components.forEach((s,o)=>{s.repository!==zw&&!i.has(s.name)&&e.addIssue({code:y.ZodIssueCode.custom,path:["components",o,"name"],message:"a ready customer_deployed plan reads what every component a repository builds runs"})})}});var WHt=["clean","recipe-drift"];function FT(t){let e=Oye.posix.normalize(t);return e===t&&(e===".skyramp/workspace.yml"||e.startsWith(".skyramp/sut/")&&e.length>13)}var Uye=[".skyramp/sut/setup.sh",".skyramp/sut/teardown.sh",".skyramp/sut/health.json",".skyramp/workspace.yml"];var UT=(e=>(e.GitHub="github",e))(UT||{}),jz=(s=>(s.Trigger="TRIGGER",s.Baseline="BASELINE",s.AuthoritativeRelatedChange="AUTHORITATIVE_RELATED_CHANGE",s.ConfiguredSharedDependency="CONFIGURED_SHARED_DEPENDENCY",s.ManualOverride="MANUAL_OVERRIDE",s))(jz||{}),GT=(s=>(s.PullRequest="pull_request",s.ReadyForReview="ready_for_review",s.Schedule="schedule",s.Manual="manual",s.SutSetup="sut_setup",s))(GT||{}),Xst=["sut_setup"],eot=["sut_setup","schedule"];var qT=(e=>(e.PullRequest="pull_request",e))(qT||{}),$T=(i=>(i.ModelProvider="model_provider",i.SutAccess="sut_access",i.Skyramp="skyramp",i.Other="other",i))($T||{});var HT=(i=>(i.ReportUploaded="report_uploaded",i.NoReport="no_report",i.Cancelled="cancelled",i.RecipeUploaded="recipe_uploaded",i))(HT||{}),Vye=(r=>(r.Running="running",r.Ended="ended",r))(Vye||{}),zT=(o=>(o.TimedOut="timed_out",o.AgentFailed="agent_failed",o.SetupFailed="setup_failed",o.CredentialMissing="credential_missing",o.RunnerLost="runner_lost",o.CheckoutFailed="checkout_failed",o))(zT||{}),Vz=(r=>(r.Onboard="onboard",r.Deploy="deploy",r))(Vz||{}),Wz=(r=>(r.Started="started",r.Finished="finished",r))(Wz||{}),Yz=(n=>(n.Passed="passed",n.Failed="failed",n.Skipped="skipped",n))(Yz||{});var jT=(n=>(n.ChooseCredential="choose_credential",n.FreeText="free_text",n.ChooseSource="choose_source",n))(jT||{});var VT=(n=>(n.Discover="discover",n.Start="start",n.Check="check",n))(VT||{}),WT=(i=>(i.Found="found",i.Starting="starting",i.Up="up",i.OutOfScope="out_of_scope",i))(WT||{}),Ql=y.string().regex(/^[1-9][0-9]{0,18}$/),eA=y.string().regex(/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/),_l=y.string().regex(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/),Xg=y.string().regex(/^[0-9a-f]{64}$/),LT=y.object({id:_l,revision:Ql}).strict(),As=y.object({provider:y.nativeEnum(UT),providerAccountId:Ql,providerRepositoryId:Ql}).strict(),zz=t=>`${t.provider}:${t.providerRepositoryId}`,Wye=As.extend({namespace:y.string().min(1),name:y.string().min(1)}).strict(),Gye=y.object({ref:y.string().min(1),revision:eA}).strict(),tot=y.object({repository:Wye,revision:eA,ref:y.string().nullable(),reason:y.nativeEnum(jz),change:y.object({kind:y.nativeEnum(qT),number:Ql,base:Gye.optional()}).strict().nullable(),compareWith:Gye.optional()}).strict();function rot(t){let e=t.baseline.sources,r=t.trigger.kind==="schedule"&&t.baseline.confirmedRunId!==null;if(!e)return r?"names every source of the snapshot, once":null;if(t.baseline.confirmedRunId===null)return"a run with no Confirmed Baseline names no baseline sources";let n=new Set(t.sources.map(o=>zz(o.repository))),i=new Set(e.map(o=>zz(o.repository)));return i.size===e.length&&i.size===n.size&&[...i].every(o=>n.has(o))?null:"names every source of the snapshot, once"}var not=y.object({schemaVersion:y.literal(1),runId:_l,skyrampOrgId:_l,target:LT,policy:LT.nullable(),trigger:y.object({kind:y.nativeEnum(GT),requestedAt:y.string().datetime()}).strict(),configuration:y.object({captureId:_l,revision:Ql.nullable(),hash:Xg}).strict(),baseline:y.object({confirmedRunId:_l.nullable(),sources:y.array(y.object({repository:As,revision:eA}).strict()).optional()}).strict(),sources:y.array(tot).min(1),execution:y.object({provider:y.nativeEnum(UT),repository:Wye,workflowPath:y.string(),ref:y.string()}).strict(),credentials:y.array(y.object({name:y.string(),purpose:y.nativeEnum($T),required:y.boolean()}).strict())}).strict().superRefine((t,e)=>{let r=t.trigger.kind,n=Xst.includes(r);n!==(t.policy===null)&&e.addIssue({code:y.ZodIssueCode.custom,path:["policy"],message:n?`a ${r} run has no policy`:"names the policy revision"});let i=rot(t);i&&e.addIssue({code:y.ZodIssueCode.custom,path:["baseline","sources"],message:i}),eot.includes(r)&&t.sources.forEach((s,o)=>{s.reason!=="BASELINE"&&e.addIssue({code:y.ZodIssueCode.custom,path:["sources",o,"reason"],message:`a ${r} run selects only BASELINE sources`})}),t.sources.forEach((s,o)=>{s.compareWith&&(s.reason!=="MANUAL_OVERRIDE"||s.change)&&e.addIssue({code:y.ZodIssueCode.custom,path:["sources",o,"compareWith"],message:"only a MANUAL_OVERRIDE source without a change names a revision to compare with"})})});var Jz=(r=>(r.Person="person",r.Policy="policy",r))(Jz||{}),iot=y.object({kind:y.nativeEnum(Jz),name:y.string().min(1).max(256)}).strict(),ezt=y.object({schemaVersion:y.literal(1),attempt:y.object({id:_l,number:y.number().int().positive(),deadlineAt:y.string().datetime()}).strict(),manifest:not,manifestHash:Xg,entitlements:y.array(y.string()).optional(),runToken:y.string().min(1).optional(),requestedBy:iot.nullable().optional(),writeAllowed:y.boolean().optional()}).strict(),tzt=y.object({schemaVersion:y.literal(1),runId:_l,execution:y.object({provider:y.nativeEnum(UT),repository:As,providerRunId:Ql,providerRunAttempt:y.number().int().positive(),workflowRef:y.string().min(1)}).strict(),requester:y.literal(!0).optional(),writeCheck:y.literal(!0).optional()}).strict(),qye=y.object({repository:As,number:Ql}).strict(),sot=y.discriminatedUnion("status",[y.object({status:y.literal("none")}).strict(),y.object({status:y.literal("delivered"),pullRequests:y.array(qye).min(1)}).strict(),y.object({status:y.literal("partial"),pullRequests:y.array(qye).min(1),failures:y.array(y.object({repository:As,message:y.string()}).strict()).min(1)}).strict(),y.object({status:y.literal("failed"),message:y.string()}).strict()]),oot=y.object({sufficiency:y.nativeEnum(Gw),rationale:y.string().min(1),gaps:y.array(y.string().min(1)),acceptedEvidence:y.array(y.object({kind:y.nativeEnum(fd),repository:y.string().min(1),revision:eA,reference:y.string().min(1),summary:y.string().min(1)}).strict()),actionRequired:y.array(y.nativeEnum(qw))}).strict().superRefine((t,e)=>{t.sufficiency==="sufficient"!=(t.gaps.length===0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["gaps"],message:"sufficient evidence has no gaps, and insufficient evidence names at least one"}),new Set(t.actionRequired).size!==t.actionRequired.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["actionRequired"],message:"a reason appears twice"})}),Zg=500,Kz=2048,Zz=1e3,aot=y.object({testResults:y.array(y.object({resultId:y.string().regex(/^[^\u0000-\u001f]{1,512}$/),repository:y.string().regex(/^[A-Za-z0-9._-]{1,100}\/[A-Za-z0-9._-]{1,100}$/),testType:y.string(),endpoint:y.string(),status:y.enum(["Pass","Fail","Skipped"]),details:y.string(),fileName:y.string().optional(),testId:y.string().regex(/^[^\u0000-\u001f]{1,512}$/).optional(),failureCause:y.object({kind:y.nativeEnum(QT),family:y.nativeEnum(PT).optional()}).strict().optional()}).strict()),validation:oot,changes:y.array(y.object({id:y.string().min(1).max(512),text:y.string().max(16384),source:y.string().max(Kz),testedBy:y.array(y.string().min(1).max(1024)).max(Zg),writtenBy:y.array(y.string().min(1).max(1024)).max(Zg),maintainedBy:y.array(y.string().min(1).max(1024)).max(Zg)}).strict()).max(Zz).optional(),traceVersion:y.literal(1)}).passthrough().superRefine((t,e)=>{let r=new Set;t.testResults.forEach((n,i)=>{let s=n.failureCause;s&&n.status==="Pass"&&e.addIssue({code:y.ZodIssueCode.custom,path:["testResults",i,"failureCause"],message:"a passing result has no failure cause"}),s&&s.kind==="environment"!=(s.family!==void 0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["testResults",i,"failureCause"],message:"an environment cause names its family, and no other cause does"});let o=["testResults",i,"resultId"],a=n.resultId.slice(`${n.repository}:${n.testType}:`.length);(!n.resultId.startsWith(`${n.repository}:${n.testType}:`)||a===""||a.startsWith("/")||a.startsWith("#"))&&e.addIssue({code:y.ZodIssueCode.custom,path:o,message:"is not repository:testType:path for this result"}),r.has(n.resultId)&&e.addIssue({code:y.ZodIssueCode.custom,path:o,message:"stable identity repeated"}),r.add(n.resultId)})}),$ye=t=>t.replace(/[%:#]/g,e=>encodeURIComponent(e));function Xz(t,e,r,n){let i=r.replace(/\\/g,"/"),s=i.split("/").filter(c=>c!==""&&c!==".");if(i.startsWith("/")||/^[A-Za-z]:/.test(i)||s.length===0||s.includes(".."))throw new Error(`testResultId needs a path relative to the repository root, got "${r}"`);let o=s.join("/"),a=`${t}:${e}:${$ye(o)}`;return n?`${a}#${$ye(n)}`:a}var Yye=50,Hye=y.string().regex(/^(?!.*\.\.)(?!\/)(?!.*\/$)[A-Za-z0-9._/-]{1,255}$/),Jye=y.discriminatedUnion("kind",[y.object({kind:y.literal("branch_pushed"),repository:As,branch:Hye,commit:eA}).strict(),y.object({kind:y.enum(["pull_request_opened","pull_request_edited"]),repository:As,number:Ql,branch:Hye}).strict()]),cot=y.object({schemaVersion:y.literal(1),run:y.object({runId:_l,attemptNumber:y.number().int().positive(),manifestHash:Xg}).strict(),target:LT,policy:LT,provenance:y.object({sources:y.array(y.object({repository:As,revision:eA}).strict())}).strict(),delivery:y.object({generatedTests:sot}).strict(),producer:y.object({testbot:y.string().regex(/^[0-9A-Za-z.+-]{1,64}$/),mcp:y.string().regex(/^[0-9A-Za-z.+-]{1,64}$/)}).strict(),report:aot,githubWrites:y.array(Jye).max(Yye).optional()}).strict(),rzt=cot.pick({schemaVersion:!0,run:!0,target:!0,provenance:!0,producer:!0}).extend({recipe:Lye}).strict().superRefine(({recipe:t},e)=>{let r=(s,o)=>e.addIssue({code:y.ZodIssueCode.custom,path:["recipe",...s],message:o});t.status==="blocked"&&t.blockers.length===0&&r(["blockers"],"a blocked recipe names at least one blocker");let n=new Set,i=0;t.files.forEach((s,o)=>{FT(s.path)||r(["files",o,"path"],"is not .skyramp/workspace.yml or a file under .skyramp/sut/"),n.has(s.path)&&r(["files",o,"path"],"file repeated"),n.add(s.path),MT.test(s.content)&&r(["files",o,"content"],"is not text");let a=Buffer.byteLength(s.content,"utf8");i+=a,a>Mf&&r(["files",o,"content"],`larger than ${Mf} bytes`)}),i>Ff&&r(["files"],`recipe text over ${Ff} bytes in total`)}),lot=y.object({schemaVersion:y.literal(1),stage:y.nativeEnum(Vz),event:y.nativeEnum(Wz),status:y.nativeEnum(Yz).optional()}).strict().refine(t=>t.status===void 0||t.event==="finished",{path:["status"],message:"only a finished stage has a status"}),Vw=100,Kye=5e3,Hz=2e3,zye=100,Zye=/^[A-Za-z0-9_-]{1,64}$/,uot=20,dot=200,Xye=100,pot=100,fot=200,hot=500,ej=/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]|[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/,mc=t=>y.string().max(t).refine(e=>e.trim().length>0&&!ej.test(e),"blank, or not text"),jw={sequence:y.number().int().min(1).max(Kye),at:y.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,6})?Z$/)},eEe=y.discriminatedUnion("kind",[y.object({...jw,kind:y.literal("log"),phase:y.nativeEnum(VT),text:mc(Hz)}).strict(),y.object({...jw,kind:y.literal("component"),name:mc(zye),repository:mc(fot).refine(t=>/^[^/\s]+\/[^/\s]+$/.test(t),"not owner/name"),file:mc(hot).nullable(),status:y.nativeEnum(WT),version:mc(pot).optional()}).strict(),y.object({...jw,kind:y.literal("reply"),to:y.string().uuid(),accepted:y.boolean(),text:mc(Hz)}).strict(),y.object({...jw,kind:y.literal("question"),id:y.string().regex(Zye),type:y.nativeEnum(jT),text:mc(Hz),component:mc(zye).nullable(),choices:y.array(mc(dot)).max(uot),placeholder:mc(Xye).optional()}).strict(),y.object({...jw,kind:y.literal("delivered"),ids:y.array(y.string().uuid()).max(Vw)}).strict()]).superRefine((t,e)=>{t.kind==="question"&&(t.type==="free_text"!=(t.choices.length===0)&&e.addIssue({code:y.ZodIssueCode.custom,path:["choices"],message:"choices exactly when the question is not free_text"}),new Set(t.choices).size!==t.choices.length&&e.addIssue({code:y.ZodIssueCode.custom,path:["choices"],message:"a choice twice"}))}),mot=y.object({schemaVersion:y.literal(1),events:y.array(eEe).max(Vw)}).strict(),jye={id:y.string().uuid(),text:y.string(),by:y.string().nullable(),at:y.string()},got=y.discriminatedUnion("kind",[y.object({...jye,kind:y.literal("instruction"),question:y.null()}),y.object({...jye,kind:y.literal("answer"),question:y.string().regex(Zye)})]),Aot=y.object({schemaVersion:y.literal(1),leftOut:y.array(y.string()),messages:y.array(got).optional()}),yot=y.object({schemaVersion:y.literal(1),configuration:y.object({captureId:_l,revision:Ql.nullable(),hash:Xg}).strict(),capture:y.object({schemaVersion:y.literal(1),settings:y.object({target:y.object({environment:y.unknown()}).passthrough(),timeoutMinutes:y.number().int().positive()}).passthrough(),provenance:y.object({}).passthrough(),hash:Xg}).strict()}).strict(),Eot=$z.extend({version:y.number().int().positive(),components:y.array(y.object({name:y.string().min(1),repository:y.string().min(1)}).strict())}).strict(),bot=y.object({schemaVersion:y.literal(1),testEnvironment:Eot.nullable().optional(),runtimePlan:y.object({version:y.number().int().positive(),contentHash:Xg,files:y.array(y.object({path:y.string().refine(FT,"not a recipe file path"),content:y.string()}).strict()).min(1)}).strict().nullable()}).strict().refine(t=>!(t.runtimePlan&&t.testEnvironment),{message:"a run pins a recipe or a customer's Test Environment, never both",path:["testEnvironment"]}),wot=y.object({schemaVersion:y.literal(1),state:y.nativeEnum(Vye),ending:y.nativeEnum(HT).nullable(),cause:y.nativeEnum(zT).nullable(),deadlineAt:y.string().datetime()}).strict();function tEe(t){return t.reportUploaded?{schemaVersion:1,reason:"report_uploaded",cause:null}:t.recipeUploaded?{schemaVersion:1,reason:"recipe_uploaded",cause:null}:t.cancelled?{schemaVersion:1,reason:"cancelled",cause:null}:{schemaVersion:1,reason:"no_report",cause:Cot(t)}}function Cot(t){return t.credentialMissing?"credential_missing":t.checkoutFailed?"checkout_failed":t.setupFailed?"setup_failed":t.timedOut?"timed_out":"agent_failed"}var Iot=["generate-unit","native-run","finalize"],Sot=["preflight","plan","write",...Iot,"report","deliver"];var tA=(n=>(n.Unit="unit",n.Integration="integration",n.E2e="e2e",n))(tA||{});var YT=1440;dt();Pz();Of();var rEe;(function(t){t[t.lineFeed=10]="lineFeed",t[t.carriageReturn=13]="carriageReturn",t[t.space=32]="space",t[t._0=48]="_0",t[t._1=49]="_1",t[t._2=50]="_2",t[t._3=51]="_3",t[t._4=52]="_4",t[t._5=53]="_5",t[t._6=54]="_6",t[t._7=55]="_7",t[t._8=56]="_8",t[t._9=57]="_9",t[t.a=97]="a",t[t.b=98]="b",t[t.c=99]="c",t[t.d=100]="d",t[t.e=101]="e",t[t.f=102]="f",t[t.g=103]="g",t[t.h=104]="h",t[t.i=105]="i",t[t.j=106]="j",t[t.k=107]="k",t[t.l=108]="l",t[t.m=109]="m",t[t.n=110]="n",t[t.o=111]="o",t[t.p=112]="p",t[t.q=113]="q",t[t.r=114]="r",t[t.s=115]="s",t[t.t=116]="t",t[t.u=117]="u",t[t.v=118]="v",t[t.w=119]="w",t[t.x=120]="x",t[t.y=121]="y",t[t.z=122]="z",t[t.A=65]="A",t[t.B=66]="B",t[t.C=67]="C",t[t.D=68]="D",t[t.E=69]="E",t[t.F=70]="F",t[t.G=71]="G",t[t.H=72]="H",t[t.I=73]="I",t[t.J=74]="J",t[t.K=75]="K",t[t.L=76]="L",t[t.M=77]="M",t[t.N=78]="N",t[t.O=79]="O",t[t.P=80]="P",t[t.Q=81]="Q",t[t.R=82]="R",t[t.S=83]="S",t[t.T=84]="T",t[t.U=85]="U",t[t.V=86]="V",t[t.W=87]="W",t[t.X=88]="X",t[t.Y=89]="Y",t[t.Z=90]="Z",t[t.asterisk=42]="asterisk",t[t.backslash=92]="backslash",t[t.closeBrace=125]="closeBrace",t[t.closeBracket=93]="closeBracket",t[t.colon=58]="colon",t[t.comma=44]="comma",t[t.dot=46]="dot",t[t.doubleQuote=34]="doubleQuote",t[t.minus=45]="minus",t[t.openBrace=123]="openBrace",t[t.openBracket=91]="openBracket",t[t.plus=43]="plus",t[t.slash=47]="slash",t[t.formFeed=12]="formFeed",t[t.tab=9]="tab"})(rEe||(rEe={}));var vot=new Array(20).fill(0).map((t,e)=>" ".repeat(e)),rA=200,Bot={" ":{"\n":new Array(rA).fill(0).map((t,e)=>`
216
216
  `+" ".repeat(e)),"\r":new Array(rA).fill(0).map((t,e)=>"\r"+" ".repeat(e)),"\r\n":new Array(rA).fill(0).map((t,e)=>`\r
217
217
  `+" ".repeat(e))}," ":{"\n":new Array(rA).fill(0).map((t,e)=>`
218
218
  `+" ".repeat(e)),"\r":new Array(rA).fill(0).map((t,e)=>"\r"+" ".repeat(e)),"\r\n":new Array(rA).fill(0).map((t,e)=>`\r
@@ -478,8 +478,8 @@ except Exception:
478
478
  `);let u=th.join(t.outputsDir,"report.json");return WV.writeFileSync(u,JSON.stringify(o,null,2)+`
479
479
  `),{report:th.relative(t.runDir,u),"report-salvage":th.relative(t.runDir,th.join(t.outputsDir,"report-salvage.json"))}}var MC=j(require("fs")),Rs=j(require("path"));var Tt=y.string().trim().min(1),Fpt=t=>{if(!t||typeof t!="object")return t;let e=t;return typeof e.skip=="string"&&e.skip.trim()&&(e.expected===void 0||e.expected===null)?{...e,expected:"(not tested)"}:t},Lpt=y.preprocess(Fpt,y.object({id:Tt.describe('requirement id + letter, e.g. "R1a"'),clause:Tt.describe("one testable part of the requirement"),expected:Tt.describe("the outcome the requirement calls for \u2014 taken from the requirement, never from what the code does"),skip:y.string().optional().describe("only when no test can check this clause here: why"),type:y.nativeEnum(yc).optional().describe("the test that checks this clause: contract / integration (API) or ui"),target:y.string().optional().describe('its "METHOD /path", or the page / flow'),steps:y.string().optional().describe("what that test does; its expected outcome is this clause's"),finding:y.string().optional().describe("the finding id, when this clause's test also catches that defect (the defect still gets a case of its own)"),code:y.string().optional().describe("path:line of the code that decides this clause, when there is one")})),Upt=y.object({id:Tt.describe('e.g. "R1"'),statement:Tt,source:y.nativeEnum(wA).describe("spec, or pr-description (also for what the change is evidently meant to do when nothing is written down)"),evidence:Tt.describe("where it is stated: file:line, a doc section, or the quoted phrase"),clauses:y.array(Lpt)}),Gpt=y.object({id:Tt.describe('e.g. "F1"'),file:Tt,line:y.number().optional(),claim:Tt.describe("the defect, in one sentence"),severity:y.nativeEnum(bA),evidence:Tt.describe("the code line, or the probe request and response, that shows it"),conflictsWith:y.array(Tt).optional().describe("requirement or clause ids it violates")}),qpt=y.object({id:Tt.describe('unique, a short kebab-case name for what the case checks, e.g. "ship-twice-409"'),type:y.nativeEnum(yc).describe("contract / integration: an API test; ui: drives the running frontend in a browser"),target:Tt.describe('"METHOD /path", or the page / flow'),clause:y.string().optional().describe("the clause id this case checks"),finding:y.string().optional().describe("the finding id this case catches"),steps:Tt.describe("what the test does"),expected:Tt.describe("the expected outcome, from the clause")}),$pt=y.object({test:Tt.describe("existing test file"),action:y.nativeEnum(Gf),reason:Tt}),rSe=y.object({outcome:y.nativeEnum(Nl).describe("clean | recipe-drift (you had to fix the recipe) | change-broke-bringup (this PR broke start-up) | bringup-failed (the environment, not the PR)"),baseUrls:y.record(y.string()).optional().describe("service \u2192 base URL of the running app"),health:y.object({url:y.string().optional(),command:y.string().optional(),output:y.string().optional()}).optional().describe("the check that shows the app serves real data, and what it returned"),teardown:y.string().optional().describe("command that stops the app"),dependencies:y.record(y.boolean()).optional().describe("every backing service: true = really running, false = mocked"),recipeFixes:y.array(y.string()).optional().describe("what you changed in the recipe"),error:y.string().optional().describe("when it did not come up: the failing command and what it printed"),suspectFiles:y.array(y.string()).optional().describe("change-broke-bringup: the changed files to blame")}),Od=y.object({schemaVersion:y.literal(1),requirements:y.array(Upt),findings:y.array(Gpt),cases:y.array(qpt).describe("a second test for a clause, and one case per finding that fails on this branch"),maintenance:y.array($pt),triage:Uf.optional().describe("the validation triage decision, made before the app is started; its behaviorIds are clause ids"),app:rSe.optional().describe("omit when no app was needed or none was started"),summary:Tt.describe("one paragraph: what the change does and where the risk is")}),nSe=(r=>(r.Backend="backend",r.Ui="ui",r))(nSe||{}),iSe=(i=>(i.Added="added",i.Updated="updated",i.Deleted="deleted",i.Unchanged="unchanged",i))(iSe||{}),Hpt=y.object({file:Tt.describe("repo-relative path; in another repo of the change, that repo's directory name then its path"),kind:y.nativeEnum(nSe),change:y.nativeEnum(iSe).describe("added: a file you created; updated / deleted / unchanged: an existing test"),covers:y.array(y.string()).describe("the plan case ids this file tests"),defect:y.boolean().optional().describe("true when it fails because the app is wrong and the test is right \u2014 kept failing on purpose"),result:y.object({exit:y.number().nullable().describe("exit code of your last run of this file; null if it could not be run"),cases:y.array(y.object({case:Tt,status:y.nativeEnum(zl)})).describe("one entry per covered case, from the runner's own output"),details:y.string().optional().describe("what failed and why, or why it was not run")}),assertions:y.array(y.object({case:Tt,evidence:Tt})).optional().describe("per covered case, the assertion line that checks its expected outcome")}),zpt=y.object({description:Tt.describe("what is wrong and how to reproduce it: endpoint or page, input, what you got vs what was expected"),severity:y.nativeEnum(bA).optional(),sourceFile:y.string().optional().describe("the app file at fault"),sourceLine:y.number().optional(),test:y.string().optional().describe("the test file that shows it")}),Md=y.object({schemaVersion:y.literal(1),tests:y.array(Hpt),notWritten:y.array(y.object({case:Tt,reason:Tt})).describe("every plan case no test covers, and why"),lint:y.object({command:y.string(),clean:y.boolean()}).optional().describe("the linter/formatter you ran on your test files, if the repo has one"),dependenciesAdded:y.array(y.object({package:Tt,manifest:Tt})).optional(),ciRunsTests:y.boolean().optional().describe("will the repo's CI pick these tests up as they are?"),commit:y.object({message:Tt.describe('"Skyramp Testbot: \u2026", subject line at most 72 characters'),prTitle:Tt.describe("at most 72 characters"),prBody:Tt.describe("what the tests cover and what they found")}),report:y.object({summary:Tt.describe("1-2 sentences for the PR author: does the change do what it says?"),issues:y.array(zpt).describe("every defect the tests found"),nextSteps:y.array(y.string())})});function KV(t,e,r=[]){let{value:n,error:i}=Vt(e,r);return i?{errors:[i]}:Wr(t,n)}function Fd(t,e,r,n=[]){let i=KV(t,e,n);if(i.value)return i;let s=FC(r);return s===void 0?i:KV(t,s,n)}function sSe(t){let e=new Set(t.cases.map(n=>n.id)),r=t.requirements.flatMap(n=>n.clauses.filter(i=>i.steps?.trim()&&!i.skip&&!e.has(i.id)).map(i=>({id:i.id,type:i.type??"integration",target:i.target?.trim()||n.id,clause:i.id,...i.finding?{finding:i.finding}:{},steps:i.steps.trim(),expected:i.expected})));return r.length?{...t,cases:[...r,...t.cases]}:t}function oSe(t){let e=t.triage;if(e?.decision!=="runtime_required")return t;let r=new Set(e.plan.filter(s=>s.mechanism==="runtime").map(s=>s.behaviorId)),n=new Set([...e.plan,...e.outOfScope??[],...e.gaps].flatMap(s=>s.behaviorId?[s.behaviorId]:[]).filter(s=>!r.has(s))),i=t.cases.filter(s=>!s.clause||!n.has(s.clause));return i.length===t.cases.length?t:{...t,cases:i}}function SN(t,e){for(let r of e){if(!r)continue;let{value:n}=KV(t,r);if(n)return n}}function aSe(t){if(t)try{let e=JSON.parse(t);if(!e||typeof e!="object"||!("app"in e)||rSe.safeParse(e.app).success)return;let{app:r,...n}=e;return JSON.stringify(n)}catch{return}}function FC(t){try{return MC.readFileSync(t,"utf-8")}catch{return}}function LA(t,e){let r=new Set;return t.filter(n=>{let i=e(n).trim();return r.has(i)?!1:(r.add(i),!0)})}function ZV(t){return t?.outcome==="clean"||t?.outcome==="recipe-drift"}var cSe=t=>t.code?.trim()||t.requirement.evidence;function XV(t){return LA(t.requirements.flatMap(e=>e.clauses.map(r=>({id:r.id,evidence:cSe({...r,requirement:e})}))),e=>e.id)}function lSe(t,e,r=new Date){let n=LA(t.requirements,f=>f.id),i=LA(n.flatMap(f=>f.clauses.map(A=>({...A,requirement:f}))),f=>f.id),s=new Set(i.map(f=>f.id)),o=LA(t.findings,f=>f.id),a=new Set(o.map(f=>f.id)),c=LA(t.cases,f=>f.id),l=ZV(t.app),u=i.map(f=>({id:f.id,statement:f.clause,surface:c.find(A=>A.clause===f.id)?.target??f.requirement.id,evidence:cSe(f),satisfies:[f.requirement.id]})),d={schemaVersion:1,repository:e.repository,project:{type:"other",primaryLanguage:"unknown",languages:[],frameworks:[]},services:[],infrastructure:{containerized:!1,hasDockerCompose:!1,composeFiles:[],hasCiCd:!1},diff:{changedFiles:e.changedFiles,nonApplicationOnly:e.nonApplicationOnly,summary:t.summary.slice(0,400)},endpoints:{new:[],modified:[],removed:[]},existingTests:{locations:[]},entityMap:{entities:[],references:[],stateFields:[]},requirements:n.map(f=>({id:f.id,statement:f.statement,source:f.source,evidence:f.evidence})),behaviors:u,findings:o.map(f=>({id:f.id,file:f.file,line:f.line,claim:f.claim,severity:f.severity,evidence:f.evidence,status:"suspected",conflictsWith:f.conflictsWith})),unexamined:[],summary:t.summary},p={schemaVersion:1,mode:l?"full":"static",maintenance:LA(t.maintenance,f=>f.test).map(f=>({test:f.test,action:f.action,evidence:f.reason})),generate:c.map(f=>({id:f.id,type:f.type,target:f.target,from_behavior:f.clause&&s.has(f.clause)?f.clause:void 0,from_finding:f.finding&&a.has(f.finding)?f.finding:void 0,rationale:i.find(A=>A.id===f.clause)?.clause??o.find(A=>A.id===f.finding)?.claim??f.steps,expected_observable:f.expected,verifiable:l})),surfaces:[],budget:{total:c.length,rationale:"planned in one session from the requirements' clauses"},summary:t.summary};return{analyze:d,recommend:p,deploy:t.app?jpt(t.app,e.defaultTeardown,r):void 0}}function jpt(t,e,r){return{schemaVersion:1,baseUrls:t.baseUrls,health:t.health?{url:t.health.url,command:t.health.command,output:t.health.output??""}:void 0,teardown:t.teardown?.trim()||e||"true",dependencies:t.dependencies,outcome:t.outcome,recipeFixes:t.recipeFixes,bringupError:t.error?{command:"bring-up",output:t.error}:void 0,suspectFiles:t.suspectFiles,seeded:!1,startedAt:r.toISOString()}}var Vpt={added:"place",updated:"update",deleted:"delete",unchanged:"verify"};function Wpt(t){return t.result.exit!==null&&t.result.exit!==0||t.result.cases.some(e=>e.status==="fail")}function tSe(t){let[e,...r]=t.split(`
480
480
  `);return[e.length<=Pd?e:`${e.slice(0,Pd-1).trimEnd()}\u2026`,...r].join(`
481
- `)}function uSe(t,e,r=[]){let n=[],i=[],s=new Set,o=new Map([...r].reverse().map(f=>[Rs.basename(Rs.resolve(f.path)),Rs.resolve(f.path)])),a=new Map;for(let f of t.tests){let A=e,m=Ft(e,f.file),w=m,C=f.file.replace(/^\.\//,"").split("/")[0],b=Rs.resolve(e,f.file),S=[...o.entries()].find(([,x])=>b.startsWith(x+Rs.sep));if(o.has(C)&&!MC.existsSync(Rs.join(e,C))?(A=o.get(C),m=Ft(A,f.file.replace(/^\.\//,"").slice(C.length+1)),w=`${C}/${m}`):S&&(A=S[1],m=Ft(A,b),w=`${S[0]}/${m}`),!m||m.startsWith("..")||Rs.isAbsolute(m)){n.push(f.file);continue}if(f.change!=="deleted"&&!MC.existsSync(Rs.join(A,m))){n.push(f.file);continue}s.has(w)||(s.add(w),a.set(w,{root:A,rel:m}),i.push({...f,file:w}))}let c=i.map(f=>{let A=Vpt[f.change];return{...A==="place"?{from:f.file}:{},file:f.file,action:A,covers:f.covers,...f.defect&&Wpt(f)?{finding:!0}:{},reran:{exit:f.result.exit,details:f.result.details,tests:f.result.cases},...A==="delete"?{}:{assertionCount:Gl(a.get(f.file).root,a.get(f.file).rel,Nd)}}}),l=f=>({schemaVersion:1,tests:i.filter(A=>A.kind===f&&A.change==="added").map(A=>({file:A.file,source:"llm",language:Hf(A.file)??"unknown",covers:A.covers,assertions:A.assertions??[]})),deferredUi:[],summary:t.report.summary}),u=t.lint?.command?.trim()??"",d=t.lint?.clean??!0,p={schemaVersion:1,lint:[{tool:u.split(/\s+/)[0]??"",command:u,clean:d,uncovered:[]}],fileConventions:{ok:!0},ciReadiness:{covered:t.ciRunsTests??!0},dependencies:{added:t.dependenciesAdded??[]},commit:{message:tSe(t.commit.message)},pr:{title:tSe(t.commit.prTitle).split(`
482
- `)[0],body:t.commit.prBody},ready:d,blockers:d?[]:[`the linter (${u||"the repo's linter"}) still reports problems in the test files`],summary:t.report.summary};return{adapt:{schemaVersion:1,items:c,ignored:[],summary:t.report.summary},generate:l("backend"),generateUi:l("ui"),finalize:p,dropped:n}}function Ypt(t,e,r,n=[]){let i=t.test;if(!i)return[];let s=e.tests.find(m=>m.file===i||i.endsWith(`/${m.file}`)||m.file.endsWith(`/${i}`));if(!s)return[];let o=new Map;for(let m of r.requirements){o.set(m.id,m);for(let w of m.clauses)o.set(w.id,m)}let a=new Set(s.result.cases.filter(m=>m.status==="fail").map(m=>m.case)),c=new Map;for(let m of s.covers.filter(w=>!a.size||a.has(w))){let w=r.cases.find(b=>b.id===m);if(!w)continue;let C=w.finding?r.findings.find(b=>b.id===w.finding):void 0;for(let b of[w.clause,...C?.conflictsWith??[]]){let S=b?o.get(b):void 0;S&&c.set(S.id,S)}}let l=n.filter(m=>m.trim()),u=m=>!l.length||l.some(w=>Kpt(m,w)),d=m=>{if(m.source==="spec")return!0;let w=dSe(m.evidence);return w!==void 0&&u(w)},p=[...c.values()].filter(d).slice(0,2),f=p.filter(m=>m.source!=="spec").map(m=>Jpt(m.evidence)),A=p.filter(m=>m.source==="spec").map(m=>m.evidence.trim());return[...f.length?[`the PR description, which says ${f.join(" and ")}`]:[],...A.length?[`the spec (${A.join("; ")})`]:[]]}function Jpt(t){let e=dSe(t);return e!==void 0?`"${e}"`:t.trim()}function Kpt(t,e){let r=hC(e),n=hC(t).split(/\.\.\.|\u2026/).map(i=>i.trim()).filter(i=>i.length>=3);return n.length>0&&n.every(i=>r.includes(i))}var Zpt=/["\u201c]([^"\u201d]{3,300})["\u201d]|(?:^|[\s:(\[])['\u2018]([^\n]{3,300}?)['\u2019](?=[\s.,;:)\]]|$)/;function dSe(t){let e=t.match(Zpt);return e?(e[1]??e[2]).trim():void 0}function pSe(t,e,r=[]){return{schemaVersion:1,businessCaseAnalysis:t.report.summary,issuesFound:t.report.issues.map(n=>({description:Xpt(n.test&&!n.description.includes(n.test)?`${n.description} (shown by ${n.test})`:n.description,e?Ypt(n,t,e,r):[]),severity:n.severity,category:"bug",sourceFile:n.sourceFile,sourceLine:n.sourceLine})),nextSteps:t.report.nextSteps}}function Xpt(t,e){let r=e.filter(n=>!t.includes(n.replace(/^the PR description, which says /,"")));return r.length?`${t}. This contradicts ${r.join(" and ")}.`.replace(/\.\. /,". "):t}function fSe(t,e){let r=GA.join(t.outputsDir,e);if(UA.existsSync(r))try{return UA.readFileSync(r,"utf-8")}catch{return}}function eft(t){let e=[],r=w=>{let C=fSe(t,w);if(C)try{return JSON.parse(C)}catch{return}},n=r("deploy.json");n?.outcome==="recipe-drift"?e.push("that the repo's recorded way of starting the app did not work as recorded \u2014 say what was wrong with it and what had to change, and that the scripts are now part of this PR's diff"):n?.outcome&&n.outcome!=="clean"&&e.push(`the deploy outcome "${n.outcome}" (in user terms: what it meant for the app and the tests)`);let i=Object.entries(n?.dependencies??{}).filter(([,w])=>w===!1).map(([w])=>w);i.length&&e.push(`each dependency that was NOT really connected this run, by name: ${i.join(", ")} \u2014 say what was not exercised because of it`);let o=r("adapt.json")?.items??[],a=o.filter(w=>w.finding&&w.file).map(w=>w.file);a.length&&e.push(`an issuesFound entry NAMING each test that caught a real bug: ${a.join(", ")}`);let c=o.filter(w=>w.placementBroken&&w.file).map(w=>w.file);c.length&&e.push(`a Skipped row + nextSteps item for each test that could not be placed/run: ${c.join(", ")}`);let l=o.filter(w=>w.repro&&w.file).map(w=>w.file);l.length&&e.push(`these tests are written to FAIL until the bug is fixed (their failure is the intended catch \u2014 say so in their details): ${l.join(", ")}`);let u=o.filter(w=>w.action!=="delete"&&w.file&&typeof w.reran?.exit=="number").map(w=>`${w.file} \u2192 ${w.reran.exit===0?"Pass":"Fail"}`);u.length&&e.push(`each executed test's row carries its real result (never Skipped): ${u.join("; ")}`);let d=o.filter(w=>w.action!=="delete"&&w.action!=="verify"&&w.file&&(w.reran?.exit===null||w.reran?.exit===void 0)).map(w=>w.file);d.length&&e.push(`these tests were NOT executed \u2014 their rows are Skipped (never Pass) and the report says they were not run against a live app: ${d.join(", ")}`);let f=(r("recommend.json")?.generate??[]).map(w=>w.id).filter(Boolean);f.length&&e.push(`every recommended case id appears as a newTestsCreated testId or is named in nextSteps/issuesFound with what happened to it: ${f.join(", ")}`),t.state.stages.finalize?.status==="failed"&&e.push("the tests could not be finalized (lint / commit-readiness) \u2014 say so in user terms"),t.state.stages.deploy?.status==="failed"&&e.push("the app could not be brought up \u2014 say so in user terms");let m=SA(t);return m.length&&e.push(`parts of the change analysis could not be verified (${m.length} unresolved check${m.length>1?"s":""}) \u2014 say so in user terms, and which requirements or findings that touches`),e}function tft(t){let e=Object.entries(t.state.stages).map(([a,c])=>` - ${a}: ${c.status}${c.reason?` (${c.reason})`:""}`),n=["analyze.json","deploy.json","recommend.json","generate.json","generate-ui.json","adapt.json","finalize.json"].map(a=>({n:a,body:fSe(t,a)})).filter(a=>a.body).map(a=>{let c=GA.join(t.outputsDir,a.n),l=a.body.length>6e3;return`### ${a.n}${l?` (truncated here \u2014 the complete file is ${c}; Read it before writing rows, the checks below read all of it)`:""}
481
+ `)}function uSe(t,e,r=[]){let n=[],i=[],s=new Set,o=new Map([...r].reverse().map(A=>{let m=Rs.basename(Rs.resolve(A.path));return[m,{name:m,root:Rs.resolve(A.path),slug:A.slug}]})),a=new Map,c=A=>`${A.repository??""}\0${A.file}`;for(let A of t.tests){let m=e,w=Ft(e,A.file),C,b,S=A.file.replace(/^\.\//,"").split("/")[0],x=Rs.resolve(e,A.file),Q=[...o.values()].find(k=>x.startsWith(k.root+Rs.sep));if(o.has(S)&&!MC.existsSync(Rs.join(e,S))?({name:b,root:m,slug:C}=o.get(S),w=Ft(m,A.file.replace(/^\.\//,"").slice(S.length+1))):Q&&({name:b,root:m,slug:C}=Q,w=Ft(m,x)),!w||w.startsWith("..")||Rs.isAbsolute(w)){n.push(A.file);continue}if(A.change!=="deleted"&&!MC.existsSync(Rs.join(m,w))){n.push(A.file);continue}let P={...A,file:w,shown:b?`${b}/${w}`:w,...C?{repository:C}:{}};s.has(c(P))||(s.add(c(P)),a.set(c(P),m),i.push(P))}let l=i.map(A=>{let m=Vpt[A.change];return{...m==="place"?{from:A.shown}:{},file:A.file,...A.repository?{repository:A.repository}:{},action:m,covers:A.covers,...A.defect&&Wpt(A)?{finding:!0}:{},reran:{exit:A.result.exit,details:A.result.details,tests:A.result.cases},...m==="delete"?{}:{assertionCount:Gl(a.get(c(A)),A.file,Nd)}}}),u=A=>({schemaVersion:1,tests:i.filter(m=>m.kind===A&&m.change==="added").map(m=>({file:m.shown,source:"llm",language:Hf(m.file)??"unknown",covers:m.covers,assertions:m.assertions??[]})),deferredUi:[],summary:t.report.summary}),d=t.lint?.command?.trim()??"",p=t.lint?.clean??!0,f={schemaVersion:1,lint:[{tool:d.split(/\s+/)[0]??"",command:d,clean:p,uncovered:[]}],fileConventions:{ok:!0},ciReadiness:{covered:t.ciRunsTests??!0},dependencies:{added:t.dependenciesAdded??[]},commit:{message:tSe(t.commit.message)},pr:{title:tSe(t.commit.prTitle).split(`
482
+ `)[0],body:t.commit.prBody},ready:p,blockers:p?[]:[`the linter (${d||"the repo's linter"}) still reports problems in the test files`],summary:t.report.summary};return{adapt:{schemaVersion:1,items:l,ignored:[],summary:t.report.summary},generate:u("backend"),generateUi:u("ui"),finalize:f,dropped:n}}function Ypt(t,e,r,n=[]){let i=t.test;if(!i)return[];let s=e.tests.find(m=>m.file===i||i.endsWith(`/${m.file}`)||m.file.endsWith(`/${i}`));if(!s)return[];let o=new Map;for(let m of r.requirements){o.set(m.id,m);for(let w of m.clauses)o.set(w.id,m)}let a=new Set(s.result.cases.filter(m=>m.status==="fail").map(m=>m.case)),c=new Map;for(let m of s.covers.filter(w=>!a.size||a.has(w))){let w=r.cases.find(b=>b.id===m);if(!w)continue;let C=w.finding?r.findings.find(b=>b.id===w.finding):void 0;for(let b of[w.clause,...C?.conflictsWith??[]]){let S=b?o.get(b):void 0;S&&c.set(S.id,S)}}let l=n.filter(m=>m.trim()),u=m=>!l.length||l.some(w=>Kpt(m,w)),d=m=>{if(m.source==="spec")return!0;let w=dSe(m.evidence);return w!==void 0&&u(w)},p=[...c.values()].filter(d).slice(0,2),f=p.filter(m=>m.source!=="spec").map(m=>Jpt(m.evidence)),A=p.filter(m=>m.source==="spec").map(m=>m.evidence.trim());return[...f.length?[`the PR description, which says ${f.join(" and ")}`]:[],...A.length?[`the spec (${A.join("; ")})`]:[]]}function Jpt(t){let e=dSe(t);return e!==void 0?`"${e}"`:t.trim()}function Kpt(t,e){let r=hC(e),n=hC(t).split(/\.\.\.|\u2026/).map(i=>i.trim()).filter(i=>i.length>=3);return n.length>0&&n.every(i=>r.includes(i))}var Zpt=/["\u201c]([^"\u201d]{3,300})["\u201d]|(?:^|[\s:(\[])['\u2018]([^\n]{3,300}?)['\u2019](?=[\s.,;:)\]]|$)/;function dSe(t){let e=t.match(Zpt);return e?(e[1]??e[2]).trim():void 0}function pSe(t,e,r=[]){return{schemaVersion:1,businessCaseAnalysis:t.report.summary,issuesFound:t.report.issues.map(n=>({description:Xpt(n.test&&!n.description.includes(n.test)?`${n.description} (shown by ${n.test})`:n.description,e?Ypt(n,t,e,r):[]),severity:n.severity,category:"bug",sourceFile:n.sourceFile,sourceLine:n.sourceLine})),nextSteps:t.report.nextSteps}}function Xpt(t,e){let r=e.filter(n=>!t.includes(n.replace(/^the PR description, which says /,"")));return r.length?`${t}. This contradicts ${r.join(" and ")}.`.replace(/\.\. /,". "):t}function fSe(t,e){let r=GA.join(t.outputsDir,e);if(UA.existsSync(r))try{return UA.readFileSync(r,"utf-8")}catch{return}}function eft(t){let e=[],r=w=>{let C=fSe(t,w);if(C)try{return JSON.parse(C)}catch{return}},n=r("deploy.json");n?.outcome==="recipe-drift"?e.push("that the repo's recorded way of starting the app did not work as recorded \u2014 say what was wrong with it and what had to change, and that the scripts are now part of this PR's diff"):n?.outcome&&n.outcome!=="clean"&&e.push(`the deploy outcome "${n.outcome}" (in user terms: what it meant for the app and the tests)`);let i=Object.entries(n?.dependencies??{}).filter(([,w])=>w===!1).map(([w])=>w);i.length&&e.push(`each dependency that was NOT really connected this run, by name: ${i.join(", ")} \u2014 say what was not exercised because of it`);let o=r("adapt.json")?.items??[],a=o.filter(w=>w.finding&&w.file).map(w=>w.file);a.length&&e.push(`an issuesFound entry NAMING each test that caught a real bug: ${a.join(", ")}`);let c=o.filter(w=>w.placementBroken&&w.file).map(w=>w.file);c.length&&e.push(`a Skipped row + nextSteps item for each test that could not be placed/run: ${c.join(", ")}`);let l=o.filter(w=>w.repro&&w.file).map(w=>w.file);l.length&&e.push(`these tests are written to FAIL until the bug is fixed (their failure is the intended catch \u2014 say so in their details): ${l.join(", ")}`);let u=o.filter(w=>w.action!=="delete"&&w.file&&typeof w.reran?.exit=="number").map(w=>`${w.file} \u2192 ${w.reran.exit===0?"Pass":"Fail"}`);u.length&&e.push(`each executed test's row carries its real result (never Skipped): ${u.join("; ")}`);let d=o.filter(w=>w.action!=="delete"&&w.action!=="verify"&&w.file&&(w.reran?.exit===null||w.reran?.exit===void 0)).map(w=>w.file);d.length&&e.push(`these tests were NOT executed \u2014 their rows are Skipped (never Pass) and the report says they were not run against a live app: ${d.join(", ")}`);let f=(r("recommend.json")?.generate??[]).map(w=>w.id).filter(Boolean);f.length&&e.push(`every recommended case id appears as a newTestsCreated testId or is named in nextSteps/issuesFound with what happened to it: ${f.join(", ")}`),t.state.stages.finalize?.status==="failed"&&e.push("the tests could not be finalized (lint / commit-readiness) \u2014 say so in user terms"),t.state.stages.deploy?.status==="failed"&&e.push("the app could not be brought up \u2014 say so in user terms");let m=SA(t);return m.length&&e.push(`parts of the change analysis could not be verified (${m.length} unresolved check${m.length>1?"s":""}) \u2014 say so in user terms, and which requirements or findings that touches`),e}function tft(t){let e=Object.entries(t.state.stages).map(([a,c])=>` - ${a}: ${c.status}${c.reason?` (${c.reason})`:""}`),n=["analyze.json","deploy.json","recommend.json","generate.json","generate-ui.json","adapt.json","finalize.json"].map(a=>({n:a,body:fSe(t,a)})).filter(a=>a.body).map(a=>{let c=GA.join(t.outputsDir,a.n),l=a.body.length>6e3;return`### ${a.n}${l?` (truncated here \u2014 the complete file is ${c}; Read it before writing rows, the checks below read all of it)`:""}
483
483
  \`\`\`json
484
484
  ${a.body.slice(0,6e3)}
485
485
  \`\`\``}).join(`
@@ -511,7 +511,7 @@ ${uft(t,e)}`}function uft(t,e){if(t.inputs.appRunning)return`${t.inputs.appRunni
511
511
  `)[0].slice(0,600);A&&(c=`${p.slug}: ${w}`),a.push({schemaVersion:1,repository:p.slug,committed:!1,pushed:!1,prAction:"failed",prFailure:"unknown",prDetail:w,unstaged:[]})}}let l=bi.join(t.outputsDir,"pr-body.md"),u=bi.join(t.outputsDir,"deliver.json"),d={...a[0],deliveries:a};if(rh.writeFileSync(u,JSON.stringify(d,null,2)+`
512
512
  `),c)throw new Error(`delivery failed in ${c}`);return{deliver:bi.relative(t.runDir,u),"pr-body":bi.relative(t.runDir,l)}}async function Fft(t,e,r,n,i,s){let o=n.path,c=ie(t,"finalize.json")?.commit?.message||r.commitMessage||"Skyramp Testbot: add tests",l=r.commitMessage||c,u=t.inputs.deliverRemote||"origin",d=s?s.headBranch||PSe(t.inputs.headBranch,t.inputs.baseBranch,o):PSe(void 0,void 0,o),p=Qft(d,t.inputs.deliverBranch),f=n.slug,A=QSe(Dft(r,t,n),{audience:"testbot-pr",requestedBy:t.inputs.requestedBy}),m=s?"pr-body.md":`pr-body-${OSe(f)}.md`,w=bi.join(t.outputsDir,m);rh.writeFileSync(w,A);let C=Ts(o,["rev-parse","HEAD"]);Ts(o,["checkout","-B",p]),Ts(o,["reset"]);let b=[];for(let F of i)try{Ts(o,["add","--",F])}catch{b.push(F)}let S=!1;try{Ts(o,["diff","--cached","--quiet"])}catch{Ts(o,["-c",`user.name=${Uw}`,"-c",`user.email=${Dz}`,"commit","-m",c]),S=!0}let x=Ts(o,["rev-parse","HEAD"]),Q,P=s?.headSha;if(S&&P&&P!==C)try{Ts(o,["checkout","-q","-B",p,P]),Ts(o,["-c",`user.name=${Uw}`,"-c",`user.email=${Dz}`,"cherry-pick","--keep-redundant-commits",x]),x=Ts(o,["rev-parse","HEAD"])}catch(F){try{Ts(o,["cherry-pick","--abort"])}catch{}Ts(o,["checkout","-q","-B",p,x]);let O=String(F.stderr??"").split(`
513
513
  `).find(H=>H.trim())||(F instanceof Error?F.message.split(`
514
- `)[0]:String(F));Q=`tests committed on the checkout's merge commit (re-cut onto ${P.slice(0,12)} failed: ${O.trim().slice(0,160)})`}let k=s?{}:{repositoryPath:o,repoSlug:f,devPr:null};if(!S){let F=await e.markTestsPrUnchanged({branch:p,...k});return{schemaVersion:1,repository:f,branch:p,remote:u,base:d,committed:!1,commitSha:x,pushed:!1,prAction:F.action==="unchanged"?"unchanged":"skipped",prUrl:F.url,prTitle:l,unstaged:b,reason:F.action==="unchanged"?"no test changes to commit \u2014 existing tests PR annotated as unchanged":"no test changes to commit \u2014 nothing pushed, no PR opened"}}let M=await e.publishTestsPr({repositoryPath:o,...k,remote:u,branch:p,base:d,commitSha:x,title:l,bodyFile:w,mode:t.inputs.deliverMode});return{schemaVersion:1,repository:f,branch:p,remote:u,base:M.base,requestedBase:d,committed:S,commitSha:x,pushed:M.pushed,...M.pushedCommits?{pushedCommits:M.pushedCommits}:{},prAction:M.action,...M.retargetedAs?{retargetedAs:M.retargetedAs}:{},prUrl:M.url,prTitle:l,...M.failure?{prFailure:M.failure,prDetail:M.detail}:{},...Q?{cutNote:Q}:{},...M.action==="skipped"&&M.detail?{reason:M.detail}:{},unstaged:b}}var sW={name:"deliver",timeoutMinutes:5,requires:["finalize","report"],validateInput:Nft,execute:Mft},Lft={...sW,requires:["report"],branchRequires:{runtime:["write"],"no-runtime":["finalize"]}};var ZKt={preflight:Dbe,onboard:ywe,analyze:Zbe,triage:SCe,deploy:fwe,recommend:vCe,generate:dIe,"generate-ui":fIe,"generate-unit":TV,"native-run":kV,adapt:QIe,finalize:jV,report:hSe,deliver:sW,plan:bSe,write:iW};var XKt="two-step",eZt=["two-step"];var tZt={preflight:"preflight",analyze:"plan",recommend:"plan",plan:"plan",deploy:"write",generate:"write","generate-ui":"write",adapt:"write",finalize:"write",write:"write",report:"report",deliver:"deliver",triage:"plan",onboard:"onboard","generate-unit":"generate-unit","native-run":"native-run"};var MSe=j(require("path"));var sZt=MSe.join(".skyramp","sut","get-auth-token.sh");var TZt=3*6e4;var kZt=`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;var Uft="https://testbot.skyramp.dev",qA={attemptId:"v3AttemptId",origin:"v3Origin",deadlineAt:"v3DeadlineAt",reportUploaded:"v3ReportUploaded",recipeUploaded:"v3RecipeUploaded",cause:"v3Cause"};var Gft=3e4;function qft(t){let e=t.ACTIONS_ID_TOKEN_REQUEST_URL,r=t.ACTIONS_ID_TOKEN_REQUEST_TOKEN;return delete t.ACTIONS_ID_TOKEN_REQUEST_URL,delete t.ACTIONS_ID_TOKEN_REQUEST_TOKEN,async n=>{if(!e||!r)throw new Error("The job cannot request an OIDC token. Give it the `id-token: write` permission.");let i=new URL(e);i.searchParams.set("audience",n);let s=await fetch(i,{headers:{Authorization:`Bearer ${r}`,Accept:"application/json"},redirect:"error",signal:AbortSignal.timeout(Gft)});if(!s.ok)throw new Error(`GitHub refused the OIDC token request: ${s.status}`);let o=(await s.json().catch(()=>({}))).value;if(typeof o!="string"||!o)throw new Error("GitHub's OIDC token response carried no token");return qs(o),o}}function $ft(t,e){return{origin:t,idToken:e,mask:r=>qs(r),log:r=>Re(r)}}var wXt={cancelled:"Skyramp cancelled this run",expired:"this attempt passed its deadline in Skyramp",ended:"Skyramp already ended this attempt"},CXt={cancelled:"cancelled",expired:"timed_out",ended:"agent_failed"};var IXt={analyze:"analyze",triage:"analyze",recommend:"recommend",generate:"generate","generate-ui":"generate","generate-unit":"generate",adapt:"generate",onboard:"execute",deploy:"execute","native-run":"execute",deliver:"deliver"},SXt={analyze:"change analysis",recommend:"test recommendations",generate:"test generation",execute:"test execution",deliver:"opening a pull request with the tests"};var xXt={onboard:"onboard",deploy:"deploy"};async function FSe(){let t=rs(qA.attemptId);if(!t)return;let e=rs(qA.reportUploaded)==="true",r=rs(qA.recipeUploaded)==="true",n=rs(qA.deadlineAt),i=rs(qA.cause)||void 0;!i&&!e&&!r&&n&&lEe(new Date(n),new Date)<=0&&(i="timed_out");let s=qft(process.env),o=await uEe({attemptId:t,reportUploaded:e,recipeUploaded:r,cause:i},$ft(rs(qA.origin)||Uft,s));Re(`Testbot V3: attempt ended; the run's outcome in Skyramp is ${o.outcome??"not final yet"}.`)}dt();var QN=j(require("crypto"));function oW(t){return Buffer.from(t).toString("base64url")}function Hft(t){return t.replace(/\\r\\n/g,`
514
+ `)[0]:String(F));Q=`tests committed on the checkout's merge commit (re-cut onto ${P.slice(0,12)} failed: ${O.trim().slice(0,160)})`}let k=s?{}:{repositoryPath:o,repoSlug:f,devPr:null};if(!S){let F=await e.markTestsPrUnchanged({branch:p,...k});return{schemaVersion:1,repository:f,branch:p,remote:u,base:d,committed:!1,commitSha:x,pushed:!1,prAction:F.action==="unchanged"?"unchanged":"skipped",prUrl:F.url,prTitle:l,unstaged:b,reason:F.action==="unchanged"?"no test changes to commit \u2014 existing tests PR annotated as unchanged":"no test changes to commit \u2014 nothing pushed, no PR opened"}}let M=await e.publishTestsPr({repositoryPath:o,...k,remote:u,branch:p,base:d,commitSha:x,title:l,bodyFile:w,mode:t.inputs.deliverMode});return{schemaVersion:1,repository:f,branch:p,remote:u,base:M.base,requestedBase:d,committed:S,commitSha:x,pushed:M.pushed,...M.pushedCommits?{pushedCommits:M.pushedCommits}:{},prAction:M.action,...M.retargetedAs?{retargetedAs:M.retargetedAs}:{},prUrl:M.url,prTitle:l,...M.failure?{prFailure:M.failure,prDetail:M.detail}:{},...Q?{cutNote:Q}:{},...M.action==="skipped"&&M.detail?{reason:M.detail}:{},unstaged:b}}var sW={name:"deliver",timeoutMinutes:5,requires:["finalize","report"],validateInput:Nft,execute:Mft},Lft={...sW,requires:["report"],branchRequires:{runtime:["write"],"no-runtime":["finalize"]}};var ZKt={preflight:Dbe,onboard:ywe,analyze:Zbe,triage:SCe,deploy:fwe,recommend:vCe,generate:dIe,"generate-ui":fIe,"generate-unit":TV,"native-run":kV,adapt:QIe,finalize:jV,report:hSe,deliver:sW,plan:bSe,write:iW};var XKt="two-step",eZt=["two-step"];var tZt={preflight:"preflight",analyze:"plan",recommend:"plan",plan:"plan",deploy:"write",generate:"write","generate-ui":"write",adapt:"write",finalize:"write",write:"write",report:"report",deliver:"deliver",triage:"plan",onboard:"onboard","generate-unit":"generate-unit","native-run":"native-run"};var MSe=j(require("path"));var sZt=MSe.join(".skyramp","sut","get-auth-token.sh");var TZt=3*6e4;var kZt=`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,8)}`;var Uft="https://testbot.skyramp.dev",qA={attemptId:"v3AttemptId",origin:"v3Origin",deadlineAt:"v3DeadlineAt",reportUploaded:"v3ReportUploaded",recipeUploaded:"v3RecipeUploaded",cause:"v3Cause"};var Gft=3e4;function qft(t){let e=t.ACTIONS_ID_TOKEN_REQUEST_URL,r=t.ACTIONS_ID_TOKEN_REQUEST_TOKEN;return delete t.ACTIONS_ID_TOKEN_REQUEST_URL,delete t.ACTIONS_ID_TOKEN_REQUEST_TOKEN,async n=>{if(!e||!r)throw new Error("The job cannot request an OIDC token. Give it the `id-token: write` permission.");let i=new URL(e);i.searchParams.set("audience",n);let s=await fetch(i,{headers:{Authorization:`Bearer ${r}`,Accept:"application/json"},redirect:"error",signal:AbortSignal.timeout(Gft)});if(!s.ok)throw new Error(`GitHub refused the OIDC token request: ${s.status}`);let o=(await s.json().catch(()=>({}))).value;if(typeof o!="string"||!o)throw new Error("GitHub's OIDC token response carried no token");return qs(o),o}}function $ft(t,e){return{origin:t,idToken:e,mask:r=>qs(r),log:r=>Re(r)}}var wXt={cancelled:"Skyramp cancelled this run",expired:"this attempt passed its deadline in Skyramp",ended:"Skyramp already ended this attempt"},CXt={cancelled:"cancelled",expired:"timed_out",ended:"agent_failed"};var IXt={analyze:"analyze",triage:"analyze",recommend:"recommend",generate:"generate","generate-ui":"generate","generate-unit":"generate",adapt:"generate",write:"generate",onboard:"execute",deploy:"execute","native-run":"execute",deliver:"deliver"},SXt={analyze:"change analysis",recommend:"test recommendations",generate:"test generation",execute:"test execution",deliver:"opening a pull request with the tests"};var xXt={onboard:"onboard",deploy:"deploy"};async function FSe(){let t=rs(qA.attemptId);if(!t)return;let e=rs(qA.reportUploaded)==="true",r=rs(qA.recipeUploaded)==="true",n=rs(qA.deadlineAt),i=rs(qA.cause)||void 0;!i&&!e&&!r&&n&&lEe(new Date(n),new Date)<=0&&(i="timed_out");let s=qft(process.env),o=await uEe({attemptId:t,reportUploaded:e,recipeUploaded:r,cause:i},$ft(rs(qA.origin)||Uft,s));Re(`Testbot V3: attempt ended; the run's outcome in Skyramp is ${o.outcome??"not final yet"}.`)}dt();var QN=j(require("crypto"));function oW(t){return Buffer.from(t).toString("base64url")}function Hft(t){return t.replace(/\\r\\n/g,`
515
515
  `).replace(/\\n/g,`
516
516
  `).trim()}function zft(t){return t.includes("-----BEGIN RSA PRIVATE KEY-----")}function jft(t){return t.includes("-----BEGIN OPENSSH PRIVATE KEY-----")}function Vft(t){let e=t.split(`
517
517
  `).map(r=>r.trim()).filter(r=>r&&!r.startsWith("-----")).join("");return Buffer.from(e,"base64")}function Wft(t){let e=Hft(t);if(jft(e))throw new Error(`githubAppPrivateKey is in OpenSSH format, but GitHub Apps issue PKCS#1 ("-----BEGIN RSA PRIVATE KEY-----") or PKCS#8 ("-----BEGIN PRIVATE KEY-----") keys. Re-download the .pem from the GitHub App's settings page.`);let r=zft(e)?"pkcs1":"pkcs8";try{return QN.createPrivateKey({key:Vft(e),format:"der",type:r})}catch(n){let i=r==="pkcs1"?"PKCS#1":"PKCS#8";throw new Error(`Failed to parse githubAppPrivateKey as a ${i} private key: ${n instanceof Error?n.message:String(n)}. Ensure the secret contains the full PEM, unmodified from the GitHub App's downloaded .pem file.`)}}function Yft(t,e,r=Date.now()){let n=Math.floor(r/1e3),i=oW(JSON.stringify({alg:"RS256",typ:"JWT"})),s=oW(JSON.stringify({iat:n-60,exp:n+540,iss:t})),o=`${i}.${s}`,a=Wft(e),c=QN.createSign("RSA-SHA256").update(o).sign(a);return`${o}.${oW(c)}`}var Jft=50,_N=class{opts;now;fetchImpl;repositories;installationId;cached;inFlight;scopeGeneration=0;constructor(e){this.opts=e,this.now=e.now??Date.now,this.fetchImpl=e.fetchImpl??fetch,this.repositories=e.repositories}setRepositories(e){this.repositories=e,this.scopeGeneration++,this.cached=void 0,this.inFlight=void 0}async getToken(){if(this.cached&&this.now()-this.cached.mintedAtMs<this.refreshThresholdMs())return this.cached.token;if(!this.inFlight){let e=this.mint().finally(()=>{this.inFlight===e&&(this.inFlight=void 0)});this.inFlight=e}return this.inFlight}refreshThresholdMs(){let e=process.env.SKYRAMP_TOKEN_REFRESH_THRESHOLD_MINUTES;return e!==void 0&&e!==""&&!isNaN(Number(e))?Number(e)*6e4:this.opts.refreshThresholdMs??Jft*6e4}async mint(){let e=this.scopeGeneration,r=this.now(),n=Yft(this.opts.appId,this.opts.privateKey,r),i=await this.discoverInstallationId(n),s=await this.request("POST",`/app/installations/${i}/access_tokens`,n,{repositories:this.repositories});return qs(s.token),e===this.scopeGeneration&&(this.cached={token:s.token,mintedAtMs:r}),s.token}async discoverInstallationId(e){if(this.installationId===void 0){let r=await this.request("GET",`/repos/${this.opts.owner}/${this.opts.repo}/installation`,e);this.installationId=r.id}return this.installationId}async request(e,r,n,i){let s=process.env.GITHUB_API_URL??"https://api.github.com",o=await this.fetchImpl(`${s}${r}`,{method:e,headers:{authorization:`Bearer ${n}`,accept:"application/vnd.github+json","user-agent":"skyramp-testbot",...i!==void 0&&{"content-type":"application/json"}},body:i===void 0?void 0:JSON.stringify(i)});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`GitHub App installation token request failed: ${e} ${r} returned ${o.status}. Check githubAppId/githubAppPrivateKey and that the App is installed on `+`${this.opts.owner}/${this.opts.repo}. ${a}`.trim())}return o.json()}};Of();async function LSe(){xge(ET(Oe("language")));try{await FSe()}catch(n){Me(`Testbot V3: could not end the attempt: ${n instanceof Error?n.message:String(n)}`)}try{let n=Oz(),i=($A.existsSync(n)?$A.readdirSync(n):[]).filter(f=>/^agent-log\.attempt-\d+\.ndjson$/.test(f)).sort((f,A)=>f.localeCompare(A,void 0,{numeric:!0})).map(f=>Vl.join(n,f)),s=Vl.join(n,Mz),o=[...i,...$A.existsSync(s)?[s]:[]],a=Vl.join(n,"testbot-result.txt"),c=Vl.join(n,"combined-result.txt"),l=Vl.join(n,"agent-stdout.txt"),u=Vl.join(n,Dye),d=new Lg;o.length>0&&await d.uploadArtifact("skyramp-agent-logs",o,n);let p=[a,c,l,u].filter(f=>$A.existsSync(f));p.length>0&&await d.uploadArtifact("skyramp-testbot-report",p,n)}catch(n){Me(`Failed to upload artifacts in post step: ${n instanceof Error?n.message:String(n)}`)}try{let n=_ge(),i=await Cye(n),s=Vl.resolve(n.workingDirectory);await Bye(i,s)}catch(n){Me(`Service teardown error: ${n instanceof Error?n.message:String(n)}`)}let t=rs("progressCommentId"),e=rs("completed"),r=rs("cancelled")==="true";if(t&&!e&&r)try{let n=Oe("githubToken"),i=Oe("githubAppId"),s=Oe("githubAppPrivateKey");delete process.env.INPUT_GITHUBAPPPRIVATEKEY;let o=n;if(i&&s){let f=Qz(process.env.GITHUB_REPOSITORY??"");if(f){let A=new _N({appId:i,privateKey:s,owner:f.owner,repo:f.repo,repositories:[f.repo]});o=()=>A.getToken()}}if(!o)return;let a=Fz(o);Rye(a);let c=parseInt(t,10),l=rs("steps"),u=l?JSON.parse(l):[],d=a.getRunUrl(),p=Tye(u,d);await a.updateComment({id:c},p),Do("Progress comment updated with cancellation notice")}catch(n){Me(`Failed to update progress comment on cancellation: ${n}`)}}process.env.NODE_ENV!=="test"&&(Qge(),LSe().catch(t=>{RD(t instanceof Error?t.message:String(t))}));0&&(module.exports={post});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skyramp/testbot-core",
3
- "version": "0.13.0-v3.60",
3
+ "version": "0.13.0-v3.62",
4
4
  "description": "Skyramp Testbot engine: AI-powered deterministic test generation, execution and maintenance",
5
5
  "main": "dist/main.js",
6
6
  "//files": "An explicit allowlist, never a glob. `dist/` is gitignored, so without this key npm's .gitignore fallback would publish a package with no engine in it; with a glob it would publish the sourcemaps, whose sourcesContent embeds every src/*.ts verbatim. eval-entry.js is a development entry and must not ship. dist/VERSION.json exists only in a release-engine.yml build. assets/gen-reference/ is the staged codegen reference corpus, which generate.ts finds at ../assets from dist/.",