@outerlayer/cli 0.28.3 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/{agent-setup-IDF3EXM4.js → agent-setup-S3KI7TER.js} +14 -14
  3. package/dist/{artifact-link-cmd-LEI6EOZE.js → artifact-link-cmd-LIJEXVN5.js} +2 -2
  4. package/dist/{bridge-JN3VJ23L.js → bridge-CP25XV3W.js} +2 -2
  5. package/dist/{build-JCYOHPLL.js → build-ZS4Y5MJU.js} +4 -4
  6. package/dist/build-info.json +1 -1
  7. package/dist/{builder-HCTVP2O7.js → builder-FDUOMHY3.js} +6 -6
  8. package/dist/{check-F2TJCQ7S.js → check-5XB22HKS.js} +42 -41
  9. package/dist/{chunk-6NFXOQKZ.js → chunk-23PFIW35.js} +1 -1
  10. package/dist/{chunk-SATUCMAJ.js → chunk-25Z6SBMQ.js} +2 -2
  11. package/dist/{chunk-W7BH5YN6.js → chunk-2KOXAMMB.js} +39 -28
  12. package/dist/{chunk-EPGMCEVP.js → chunk-2YLKMVFV.js} +2 -2
  13. package/dist/{chunk-QBF462QH.js → chunk-3KMYWLMK.js} +1 -1
  14. package/dist/{chunk-5B7BEQPR.js → chunk-427AVP6J.js} +2 -2
  15. package/dist/{chunk-VWFOKRYS.js → chunk-42TFWHFF.js} +1 -1
  16. package/dist/{chunk-X7SFJZIG.js → chunk-6AG2XW5S.js} +29 -10
  17. package/dist/{chunk-BUP5N3X6.js → chunk-6XQPCQBW.js} +1 -1
  18. package/dist/{chunk-WL3QF3QF.js → chunk-77YT7YC4.js} +18 -16
  19. package/dist/{chunk-EZ3DZXUH.js → chunk-7H7RUQCZ.js} +3 -3
  20. package/dist/{chunk-LRFU6RPD.js → chunk-A2E7MU2H.js} +55 -47
  21. package/dist/{chunk-MFI55TNI.js → chunk-BFA2LNJG.js} +22 -6
  22. package/dist/{chunk-N5EEEKT6.js → chunk-C7F6U2YZ.js} +3 -2
  23. package/dist/{chunk-M6NNXKRM.js → chunk-CU7TJL6S.js} +1 -1
  24. package/dist/{chunk-T7LJ6AIC.js → chunk-D3D2CERC.js} +5 -5
  25. package/dist/{chunk-RKOTJQVG.js → chunk-DXI6A6JN.js} +10 -4
  26. package/dist/{chunk-KJIUFOC4.js → chunk-F72ABZRD.js} +1 -1
  27. package/dist/{chunk-UXLLWIA2.js → chunk-FA6XWYRH.js} +7 -7
  28. package/dist/{chunk-P2RWZCV7.js → chunk-FUGRIAG7.js} +2 -2
  29. package/dist/{chunk-JYVBEWBC.js → chunk-GG3WS4C5.js} +3 -3
  30. package/dist/{chunk-O47BTZSN.js → chunk-GI73HOI2.js} +1 -1
  31. package/dist/{chunk-2BZTGJII.js → chunk-GJJDPZS2.js} +7 -7
  32. package/dist/{chunk-QKXF7CLR.js → chunk-GJOF6357.js} +4 -4
  33. package/dist/{chunk-TG7FAHRP.js → chunk-H6DGJBXG.js} +5 -5
  34. package/dist/{chunk-QFNFBQCG.js → chunk-HRWL7NSJ.js} +5 -5
  35. package/dist/chunk-I3LTIAOR.js +11 -0
  36. package/dist/{chunk-OOUHNKKX.js → chunk-IG45QISA.js} +2 -2
  37. package/dist/{chunk-AV5MJY45.js → chunk-IGEJUPDE.js} +3 -3
  38. package/dist/{chunk-QZBXLLTV.js → chunk-JL6VZHAJ.js} +38 -1
  39. package/dist/{chunk-RXFKXZJS.js → chunk-KBIJZAH4.js} +1 -1
  40. package/dist/{chunk-UHU7H57K.js → chunk-LKN4J37L.js} +151 -34
  41. package/dist/{chunk-ILU6664L.js → chunk-M4QY7YZ5.js} +360 -74
  42. package/dist/{chunk-4SJ57DH7.js → chunk-MO2NI6W6.js} +35 -27
  43. package/dist/{chunk-HWOU2HDH.js → chunk-MX3PPJNS.js} +1 -1
  44. package/dist/{chunk-ITAIEJ2T.js → chunk-MZJAIRXM.js} +4 -4
  45. package/dist/{chunk-YYGXCRIW.js → chunk-NAHTO4XM.js} +1 -1
  46. package/dist/{chunk-JGML2VUN.js → chunk-NX2G6PH6.js} +1 -1
  47. package/dist/{chunk-OO6OADZN.js → chunk-OD6WUNKZ.js} +3 -3
  48. package/dist/{chunk-DSKOHPXN.js → chunk-OKAJSYZ2.js} +1 -1
  49. package/dist/{chunk-DWVAG73B.js → chunk-PAHZQQB3.js} +1 -1
  50. package/dist/{chunk-NMIYK2G2.js → chunk-PGA5BVHQ.js} +3 -3
  51. package/dist/{chunk-YTR435DO.js → chunk-PP7C7JQO.js} +3 -3
  52. package/dist/{chunk-DQYMQ47E.js → chunk-S2Q65ZOT.js} +1 -1
  53. package/dist/{chunk-R7TMTFJP.js → chunk-SHVE5JZO.js} +1 -1
  54. package/dist/{chunk-JZJLKTZT.js → chunk-SN3C57RV.js} +5 -5
  55. package/dist/{chunk-TOHKSZKI.js → chunk-TZGQQY7Y.js} +7 -7
  56. package/dist/{chunk-VM6BX2WJ.js → chunk-UPKFSNSU.js} +5 -5
  57. package/dist/{chunk-TCBVV2JK.js → chunk-V47KJGNE.js} +3 -3
  58. package/dist/{chunk-NKD5BNRX.js → chunk-VT3BKV5D.js} +3 -3
  59. package/dist/{chunk-HOU33DW5.js → chunk-VY4YVFL5.js} +1 -1
  60. package/dist/{chunk-SVODOMYC.js → chunk-WEMNLTWB.js} +7 -7
  61. package/dist/{chunk-DIQN5SEL.js → chunk-YSH4KCUJ.js} +1 -1
  62. package/dist/{claude-launcher-LVYZ4YJI.js → claude-launcher-SHIQTBS6.js} +5 -5
  63. package/dist/{cli-5LLFR2SX.js → cli-EN2WGVM6.js} +126 -120
  64. package/dist/{command-YXOMIXOJ.js → command-2VFWA2RF.js} +30 -30
  65. package/dist/{config-TNV5HHLO.js → config-HJQ4YE57.js} +2 -2
  66. package/dist/{connect-cmd-43IWARIV.js → connect-cmd-T6IAT5FT.js} +12 -12
  67. package/dist/{container-start-YG45GLFE.js → container-start-C5ZCXXMF.js} +29 -29
  68. package/dist/{context-cleanup-LTH77FC3.js → context-cleanup-MV3X6NSF.js} +3 -3
  69. package/dist/{control-plane-launch-TIK265OT.js → control-plane-launch-5FZ6LJOC.js} +10 -10
  70. package/dist/{dist-JFVKLUHL.js → dist-H5IYGH3T.js} +2 -2
  71. package/dist/{docker-2IIRBQAB.js → docker-RFN6UWTT.js} +6 -6
  72. package/dist/{doctor-3SRHLRF5.js → doctor-VXDBXKSL.js} +32 -32
  73. package/dist/{emit-artifact-cmd-RM2X3PBJ.js → emit-artifact-cmd-M52RLUBU.js} +16 -16
  74. package/dist/{emit-cmd-QUABLVO6.js → emit-cmd-HTNETHAE.js} +6 -6
  75. package/dist/{emit-criteria-cmd-ZUI7DFND.js → emit-criteria-cmd-APDCA6FK.js} +13 -13
  76. package/dist/{emit-finding-cmd-W2FU4CCF.js → emit-finding-cmd-XEX5XVVI.js} +13 -13
  77. package/dist/{emit-result-cmd-JINMQ7QG.js → emit-result-cmd-HFC3WDWB.js} +52 -25
  78. package/dist/{emit-step-cmd-7FZWQTV3.js → emit-step-cmd-LTRPF2YX.js} +14 -14
  79. package/dist/{exec-client-6ZJJ4KVH.js → exec-client-GHFNXOY6.js} +3 -3
  80. package/dist/{fleet-readiness-3A67MUAU.js → fleet-readiness-SJA7NFR2.js} +12 -12
  81. package/dist/{governing-source-KQMKM5CI.js → governing-source-PBYOKKAJ.js} +17 -17
  82. package/dist/{guest-init-A4HHI2UR.js → guest-init-MQRIALWR.js} +4 -4
  83. package/dist/{hook-fast-UJPAWDAO.js → hook-fast-JKYCVO4R.js} +1 -1
  84. package/dist/{host-key-NGRCMJZ7.js → host-key-DBYH3RTZ.js} +3 -3
  85. package/dist/{image-tag-TEOKZVD5.js → image-tag-XNODAHAW.js} +3 -3
  86. package/dist/index.js +4 -4
  87. package/dist/{init-XP55Q4ZT.js → init-JJPJ2XLL.js} +29 -29
  88. package/dist/{init-HQW3LHIF.js → init-ZUMSDYMW.js} +29 -29
  89. package/dist/{init-cmd-XSOVMNNE.js → init-cmd-342RW7DZ.js} +22 -22
  90. package/dist/{init-update-MZ4IRFC7.js → init-update-4MCW5W72.js} +21 -21
  91. package/dist/{install-NPTR35MG.js → install-GODA5G7U.js} +4 -4
  92. package/dist/{install-cmd-4PXPAFQX.js → install-cmd-YNQ5DCZP.js} +5 -5
  93. package/dist/{layout-BBTQPS6V.js → layout-DBAL2HTQ.js} +4 -4
  94. package/dist/{login-browser-WSOSQH2T.js → login-browser-B6E4GNIF.js} +2 -2
  95. package/dist/{logs-Z4L4I3OR.js → logs-KAG3MAUO.js} +2 -2
  96. package/dist/loop-ZXJFOVCS.js +81 -0
  97. package/dist/{machine-ATDPHWL5.js → machine-F3TVUBZY.js} +25 -25
  98. package/dist/{machine-skills-O7W6NMWB.js → machine-skills-6MLTVBCF.js} +4 -4
  99. package/dist/{mcp-serve-cmd-NQM2WDYI.js → mcp-serve-cmd-SGISNCB3.js} +4 -4
  100. package/dist/{paths-2VSGK6E2.js → paths-PG4JNLZG.js} +2 -2
  101. package/dist/{pending-SFZ2AYSK.js → pending-GG252ERT.js} +3 -3
  102. package/dist/{pidfile-3FT5OLUE.js → pidfile-7SYDMT2O.js} +3 -3
  103. package/dist/{record-7SSRLSI2.js → record-ZGUOOYJH.js} +3 -3
  104. package/dist/{runner-env-2Z2DVSUJ.js → runner-env-WI6LKWIU.js} +5 -5
  105. package/dist/{runner-identity-B37HHOZR.js → runner-identity-JIC54L5Q.js} +6 -6
  106. package/dist/{runner-key-SICUOQY5.js → runner-key-RXCKMQRL.js} +29 -29
  107. package/dist/{session-sender-7MSVJZ6Q.js → session-sender-JAAMA5E6.js} +17 -17
  108. package/dist/{spec-cmd-KAQ7MX4O.js → spec-cmd-BUFLEQ6L.js} +14 -14
  109. package/dist/{starter-pack-5SOT5V4I.js → starter-pack-I5W5JAYT.js} +5 -5
  110. package/dist/{status-AI22UPKC.js → status-E7V365CD.js} +33 -32
  111. package/dist/{sync-cmd-7K3WVQKO.js → sync-cmd-U5FUBXJY.js} +16 -16
  112. package/dist/{watch-NCYESLO2.js → watch-AOPZJOUM.js} +16 -16
  113. package/dist/{work-claim-cmd-IOUEHYIY.js → work-claim-cmd-Z2UZD37I.js} +17 -17
  114. package/dist/{work-cmd-FOI74X5B.js → work-cmd-XLVQDNTC.js} +14 -14
  115. package/dist/{work-comment-cmd-3C44H4CG.js → work-comment-cmd-THLQUSMH.js} +14 -14
  116. package/dist/{work-logs-cmd-YGAWCX3X.js → work-logs-cmd-2NFG6ZY6.js} +14 -14
  117. package/dist/{work-open-pr-cmd-HUG5HAVC.js → work-open-pr-cmd-J22A2YCD.js} +15 -15
  118. package/dist/{work-pr-cmd-L2EDOYXH.js → work-pr-cmd-SYAENEH4.js} +14 -14
  119. package/dist/{work-stop-cmd-GGDVKC67.js → work-stop-cmd-FP453YCB.js} +14 -14
  120. package/package.json +1 -1
  121. package/skill-pack/maintained/outerlayer-amend/SKILL.md +40 -39
  122. package/skill-pack/maintained/outerlayer-build/SKILL.md +5 -9
  123. package/skill-pack/maintained/outerlayer-build/references/criteria-judge.md +15 -12
  124. package/skill-pack/maintained/outerlayer-build/references/release.md +11 -6
  125. package/skill-pack/shipped.json +1 -0
  126. package/skill-pack/validators/acceptance-criteria.yaml +7 -2
  127. package/dist/loop-QQBLJ7GN.js +0 -80
@@ -1,31 +1,33 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire } from 'node:module';
3
3
  import { readsJobState, READ_JOB_STATE_FORMATS, jobStateFormatOf } from './chunk-ZVB57WI6.js';
4
- import { DOCKERD_LOG, HELPER_PATH, HELPER_RECORD } from './chunk-OO6OADZN.js';
5
- import { senderPidfile } from './chunk-YTR435DO.js';
6
- import { installScrubContext } from './chunk-2BZTGJII.js';
7
- import { writeDockerClientConfig } from './chunk-RXFKXZJS.js';
8
- import { ADDED_USER, shellQuote, GH_CONFIG_YML, ghHostsYml, AssetError, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createLeaseChannel, createBuildDisk, machineConfig, resolveAgentUser, AgentUserError, runBuildSteps, LEASE_SECONDS, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, containerSenderCommand, signalInContainer, syncInContainer, copyTranscriptsFromContainer, senderAliveScript, helperFirecrackerNote, HelperRefusal, isLiteralAddress, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, DeliveryFailure, EVAL_INPUT_NAME, copyTranscripts, senderSignalScript, containerJudgeEnvironment, copyJudgeTree } from './chunk-4SJ57DH7.js';
9
- import { waitForAgent, vmExecArgs } from './chunk-WL3QF3QF.js';
10
- import { socketPathProblem, vsockLinks, LEASE_LINK, linkVsockSockets } from './chunk-DIQN5SEL.js';
11
- import { describeCallFailure, resolveGoverningSource, refusalCode, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, logStamp, createRunnerLog, removeKeptContext, formatDuration, retrySend, formatClock, keptContextTree } from './chunk-JZJLKTZT.js';
12
- import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-YYGXCRIW.js';
13
- import { readBranchHead, fetchCommit, diffBetween } from './chunk-TCBVV2JK.js';
4
+ import { DOCKERD_LOG, HELPER_PATH, HELPER_RECORD } from './chunk-OD6WUNKZ.js';
5
+ import { zodProblem } from './chunk-I3LTIAOR.js';
6
+ import { senderPidfile } from './chunk-PP7C7JQO.js';
7
+ import { installScrubContext } from './chunk-GJJDPZS2.js';
8
+ import { writeDockerClientConfig } from './chunk-KBIJZAH4.js';
9
+ import { ADDED_USER, shellQuote, GH_CONFIG_YML, ghHostsYml, AssetError, obtainImage, ensureRootfs, gitRemoteFor, startServicesFor, createLeaseChannel, createBuildDisk, machineConfig, resolveAgentUser, AgentUserError, runBuildSteps, LEASE_SECONDS, createContainerRuntime, stopContainer, cleanupContainer, outOfMemoryReason, containerSenderCommand, signalInContainer, syncInContainer, copyTranscriptsFromContainer, senderAliveScript, helperFirecrackerNote, HelperRefusal, isLiteralAddress, deleteBuildDisk, startCheckContainer, lifecycleSteps, lifecycleFailureReason, writeEvalInput, DeliveryFailure, EVAL_INPUT_NAME, copyTranscripts, senderSignalScript, containerJudgeEnvironment, copyJudgeTree } from './chunk-MO2NI6W6.js';
10
+ import { waitForAgent, vmExecArgs } from './chunk-77YT7YC4.js';
11
+ import { socketPathProblem, vsockLinks, LEASE_LINK, linkVsockSockets } from './chunk-YSH4KCUJ.js';
12
+ import { describeCallFailure, resolveGoverningSource, refusalCode, removeStagedContext, stageGovernedContext, pinnedSourceOf, commandWithShim, logStamp, createRunnerLog, removeKeptContext, formatDuration, retrySend, formatClock, keptContextTree } from './chunk-SN3C57RV.js';
13
+ import { isBuiltinContainer, isBuiltinProcess, isBuiltinVm, DEFAULT_CHECK_TIME_LIMIT_MINUTES } from './chunk-NAHTO4XM.js';
14
+ import { readBranchHead, fetchCommit, diffBetween } from './chunk-V47KJGNE.js';
14
15
  import { isSameProcess, startTimeOf } from './chunk-JBJTC3UU.js';
15
- import { HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, writeJobFile, readJobFile, childEnvironment, judgeCommand, JUDGE_MAX_TURNS, MAX_REPORT_BYTES, JUDGE_DIR, COMMAND_DIR } from './chunk-MFI55TNI.js';
16
+ import { HASH_ARGV, parseProgramHash, JUDGE_PROGRAM, writeJobFile, readJobFile, childEnvironment, withAnswerProtocol, judgeCommand, MAX_REPORT_BYTES, JUDGE_MAX_TURNS, JUDGE_DIR, COMMAND_DIR, MAX_ITEMS_BYTES } from './chunk-BFA2LNJG.js';
16
17
  import { parseSize } from './chunk-YJLHUKYS.js';
17
18
  import { CLAUDE_CODE_VERSION, FIRECRACKER_VERSION } from './chunk-SJZL5F3F.js';
18
- import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-X7SFJZIG.js';
19
- import { planArtifactUpload } from './chunk-BUP5N3X6.js';
20
- import { scrubText } from './chunk-UHU7H57K.js';
19
+ import { WorkCommandError, postJson, isSignatureRefusal, getJson } from './chunk-6AG2XW5S.js';
20
+ import { planArtifactUpload } from './chunk-6XQPCQBW.js';
21
+ import { scrubText } from './chunk-LKN4J37L.js';
21
22
  import { readCloudConfig } from './chunk-PJDQPF5N.js';
22
- import { signerFor } from './chunk-R7TMTFJP.js';
23
- import { RUNNER_FILE_MODE, runnerDir, buildDir, vsockPath } from './chunk-DWVAG73B.js';
24
- import { JOB_MOUNT, claudeCredential, repositoriesOf, primaryOf, buildEnvironment, CHECKOUT_DIR, parseIsolation, proxyEnvironment, withProxyWarningHidden, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, withHooksPath, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, authorEnvironment, withTunnelRecord, commitIdentityRecord, RELAY_PORT, repositoryPath, lifecycleWorkdir, currentClaudeCredential, removedClaudeCredentialValues, checkEnvironment, EVAL_INPUT_DIR } from './chunk-N5EEEKT6.js';
25
- import { resolvePackRoot } from './chunk-JGML2VUN.js';
23
+ import { signerFor } from './chunk-SHVE5JZO.js';
24
+ import { RUNNER_FILE_MODE, runnerDir, buildDir, vsockPath } from './chunk-PAHZQQB3.js';
25
+ import { JOB_MOUNT, claudeCredential, repositoriesOf, primaryOf, buildEnvironment, CHECKOUT_DIR, parseIsolation, proxyEnvironment, withProxyWarningHidden, containerLocalAddress, destinationEnvironment, CLAUDE_CREDENTIAL_VARIABLES, builtInEnvironment, withHooksPath, startBuildServices, BUILT_IN_NAMES, AGENT_CREDENTIAL_MISSING, sharedUserEnvironment, headerVariables, authorEnvironment, withTunnelRecord, commitIdentityRecord, RELAY_PORT, repositoryPath, lifecycleWorkdir, CHECK_ITEMS_FILE, currentClaudeCredential, removedClaudeCredentialValues, checkEnvironment, EVAL_INPUT_DIR } from './chunk-C7F6U2YZ.js';
26
+ import { resolvePackRoot } from './chunk-NX2G6PH6.js';
26
27
  import { MAINTAINED_SKILLS } from './chunk-CDDED64E.js';
27
28
  import { resolveVersion } from './chunk-RA3O54FC.js';
28
- import { parsePolicy } from './chunk-RKOTJQVG.js';
29
+ import { parsePolicy } from './chunk-DXI6A6JN.js';
30
+ import { CheckItemsSchema, CHECK_ITEM_REASON_MAX_LENGTH } from './chunk-JL6VZHAJ.js';
29
31
  import { homeFromEnv } from './chunk-VNVZDWO3.js';
30
32
  import { fdName } from './chunk-XD4ZCMA4.js';
31
33
  import { RUNNER_WORDED_REASONS, UNKNOWN_TRACKING_REASON } from './chunk-MESOOLF4.js';
@@ -821,6 +823,12 @@ function microVmJudgeEnvironment(input) {
821
823
  const needsVm = () => {
822
824
  if ((state.firecracker !== void 0 || state.jailed !== void 0) && !isRunning(runtime, state)) throw new Error("the build's microVM is gone");
823
825
  };
826
+ const readJudgeFile = async (name, maxBytes) => {
827
+ needsVm();
828
+ const file = `${GUEST_JUDGE_DIR}/${name}`;
829
+ const read = await asRoot(runtime, state, ["/bin/sh", "-c", `[ -f ${file} ] && [ ! -L ${file} ] && head -c ${maxBytes + 1} ${file}`]);
830
+ return read.status === 0 && Buffer.byteLength(read.stdout) <= maxBytes ? read.stdout : void 0;
831
+ };
824
832
  return {
825
833
  stop: async () => {
826
834
  needsVm();
@@ -865,12 +873,8 @@ function microVmJudgeEnvironment(input) {
865
873
  appendFileSync(opts.logPath, ran.stdout + ran.stderr);
866
874
  return ran;
867
875
  },
868
- readReport: async () => {
869
- needsVm();
870
- const file = `${GUEST_JUDGE_DIR}/report.html`;
871
- const read = await asRoot(runtime, state, ["/bin/sh", "-c", `[ -f ${file} ] && [ ! -L ${file} ] && head -c ${MAX_REPORT_BYTES + 1} ${file}`]);
872
- return read.status === 0 && Buffer.byteLength(read.stdout) <= MAX_REPORT_BYTES ? read.stdout : void 0;
873
- }
876
+ readReport: () => readJudgeFile("report.html", MAX_REPORT_BYTES),
877
+ readItems: () => readJudgeFile("items.json", MAX_ITEMS_BYTES)
874
878
  };
875
879
  }
876
880
  async function startCheckMicroVm(input) {
@@ -1538,10 +1542,144 @@ async function provisionProcess(input) {
1538
1542
  function cleanupProcess(runtime, state) {
1539
1543
  return closeHostBuild(runtime, state);
1540
1544
  }
1545
+ var ARTIFACT_PROOF_REASON = "Its declared artifact proves it; the judge does not judge artifacts.";
1546
+ var NO_CITING_TEST_REASON = "No test cites this criterion, and it declares no artifact proof.";
1547
+ var NO_VERDICT_REASON = "The judge gave no result for this criterion.";
1548
+ function criterionInputs(input) {
1549
+ const paths = [...new Set((input.citations[input.criterion.id] ?? []).map((citation) => citation.path))].sort();
1550
+ const hash = createHash("sha256");
1551
+ const part = (text2) => {
1552
+ hash.update(`${Buffer.byteLength(text2)}:`);
1553
+ hash.update(text2);
1554
+ };
1555
+ part("judge-criterion-v1");
1556
+ part(input.brief);
1557
+ part(input.criterion.id);
1558
+ part(input.criterion.text);
1559
+ part(input.criterion.proof ?? "");
1560
+ for (const path of paths) {
1561
+ part(path);
1562
+ part(input.testFiles.get(path) ?? "");
1563
+ }
1564
+ return hash.digest("hex");
1565
+ }
1566
+ var HTTPS_LINK = /^https:\/\/\S+$/;
1567
+ function refsFor(id, citations) {
1568
+ const refs = (citations[id] ?? []).slice(0, 20).map((citation) => ({
1569
+ path: citation.path,
1570
+ line: citation.line,
1571
+ // Only an https link becomes an href on a page; any other is left off rather than refusing the check.
1572
+ ...HTTPS_LINK.test(citation.href ?? "") ? { href: citation.href } : {}
1573
+ }));
1574
+ return refs.length === 0 ? void 0 : refs;
1575
+ }
1576
+ function planCriteria(input) {
1577
+ const previousById = new Map(input.previous.map((item) => [item.id, item]));
1578
+ const settled = [];
1579
+ const toJudge = [];
1580
+ for (const criterion of input.criteria) {
1581
+ const inputs = input.inputs.get(criterion.id);
1582
+ const base = { id: criterion.id, criterion: criterion.id, ...inputs === void 0 ? {} : { inputs } };
1583
+ const refs = refsFor(criterion.id, input.citations);
1584
+ if (criterion.proof !== null) {
1585
+ settled.push({ ...base, result: "skipped", reason: ARTIFACT_PROOF_REASON });
1586
+ continue;
1587
+ }
1588
+ if (refs === void 0) {
1589
+ settled.push({ ...base, result: "fail", reason: NO_CITING_TEST_REASON });
1590
+ continue;
1591
+ }
1592
+ const kept = previousById.get(criterion.id);
1593
+ if (kept !== void 0 && inputs !== void 0 && kept.inputs === inputs && (kept.result === "pass" || kept.result === "fail")) {
1594
+ settled.push({ ...base, result: kept.result, ...kept.reason === void 0 ? {} : { reason: kept.reason }, refs });
1595
+ continue;
1596
+ }
1597
+ toJudge.push(criterion);
1598
+ }
1599
+ return { settled, toJudge };
1600
+ }
1601
+ var MAX_ANSWER_ENTRIES = 500;
1602
+ function readAnswer(raw) {
1603
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return { ok: false, problem: "expected an object" };
1604
+ const { result, items } = raw;
1605
+ if (result !== void 0 && result !== "pass" && result !== "fail") return { ok: false, problem: 'result: must be "pass" or "fail"' };
1606
+ if (items !== void 0 && !Array.isArray(items)) return { ok: false, problem: "items: must be an array" };
1607
+ const entries = items ?? [];
1608
+ if (entries.length > MAX_ANSWER_ENTRIES) return { ok: false, problem: `items: at most ${MAX_ANSWER_ENTRIES} entries` };
1609
+ const read = [];
1610
+ for (const [index, entry] of entries.entries()) {
1611
+ const { id, result: itemResult, reason } = typeof entry === "object" && entry !== null ? entry : {};
1612
+ if (typeof id !== "string" || id === "" || id.length > 200) return { ok: false, problem: `items.${index}.id: must be text of 1 to 200 characters` };
1613
+ if (itemResult !== "pass" && itemResult !== "fail") return { ok: false, problem: `items.${index}.result: must be "pass" or "fail"` };
1614
+ if (reason !== void 0 && typeof reason !== "string") return { ok: false, problem: `items.${index}.reason: must be text` };
1615
+ read.push({ id, result: itemResult, ...reason === void 0 ? {} : { reason } });
1616
+ }
1617
+ return { ok: true, answer: { ...result === void 0 ? {} : { result }, items: read } };
1618
+ }
1619
+ function parseJudgeAnswer(text2) {
1620
+ if (text2 === void 0 || text2.trim() === "") return void 0;
1621
+ let raw;
1622
+ try {
1623
+ raw = JSON.parse(text2);
1624
+ } catch {
1625
+ return { ok: false, reason: "the judge's items.json is not JSON" };
1626
+ }
1627
+ const read = readAnswer(raw);
1628
+ return read.ok ? read : { ok: false, reason: `the judge's items.json does not match its shape: ${read.problem}` };
1629
+ }
1630
+ function oneLineReason(text2) {
1631
+ if (text2 === void 0) return void 0;
1632
+ const line = text2.replace(/\s+/g, " ").trim();
1633
+ if (line === "") return void 0;
1634
+ return line.length <= CHECK_ITEM_REASON_MAX_LENGTH ? line : `${line.slice(0, CHECK_ITEM_REASON_MAX_LENGTH - 1)}\u2026`;
1635
+ }
1636
+ function judgedItems(input) {
1637
+ return input.toJudge.map((criterion) => {
1638
+ const said = input.results.get(criterion.id);
1639
+ const inputs = input.inputs.get(criterion.id);
1640
+ const refs = refsFor(criterion.id, input.citations);
1641
+ const reason = said === void 0 ? NO_VERDICT_REASON : oneLineReason(said.reason);
1642
+ return {
1643
+ id: criterion.id,
1644
+ criterion: criterion.id,
1645
+ result: said?.result ?? "fail",
1646
+ ...reason === void 0 ? {} : { reason },
1647
+ ...refs === void 0 ? {} : { refs },
1648
+ ...inputs === void 0 || said === void 0 ? {} : { inputs }
1649
+ };
1650
+ });
1651
+ }
1652
+ function inCriteriaOrder(items, criteria) {
1653
+ const order = new Map(criteria.map((criterion, index) => [criterion.id, index]));
1654
+ return [...items].sort((a, b) => (order.get(a.id) ?? Number.MAX_SAFE_INTEGER) - (order.get(b.id) ?? Number.MAX_SAFE_INTEGER));
1655
+ }
1656
+ function itemsSentence(items, noun) {
1657
+ const failing = items.filter((item) => item.result === "fail").map((item) => item.id);
1658
+ if (failing.length === 0) return void 0;
1659
+ return `${failing.length} of ${items.length} ${items.length === 1 ? noun.one : noun.many} not satisfied: ${failing.join(", ")}`;
1660
+ }
1661
+
1662
+ // src/runner/host-checks.ts
1541
1663
  var MAX_VALIDATOR_FILES = 20;
1542
1664
  var VALIDATORS_DIR = ".outerlayer/validators";
1543
1665
  var POLICY_FILE = ".outerlayer/policy.yaml";
1544
1666
  var LIMIT_KEY_TAIL = " (runner.checks.timeLimitMinutes)";
1667
+ var MAX_CHECK_ITEMS_BYTES = 1024 * 1024;
1668
+ function commandItems(read, scrub) {
1669
+ if (read.status !== 0 || read.stdout.trim() === "") return { ok: true, items: void 0 };
1670
+ if (Buffer.byteLength(read.stdout) > MAX_CHECK_ITEMS_BYTES) return { ok: false, reason: `its check items file is longer than ${MAX_CHECK_ITEMS_BYTES} bytes` };
1671
+ let raw;
1672
+ try {
1673
+ raw = JSON.parse(read.stdout);
1674
+ } catch {
1675
+ return { ok: false, reason: "its check items file (OUTERLAYER_CHECK_ITEMS) is not JSON" };
1676
+ }
1677
+ const parsed = CheckItemsSchema.safeParse(raw);
1678
+ if (!parsed.success) {
1679
+ return { ok: false, reason: `its check items file does not match the check item shape: ${zodProblem(parsed.error.issues)}` };
1680
+ }
1681
+ return { ok: true, items: parsed.data.map((item) => item.reason === void 0 ? item : { ...item, reason: scrub(item.reason) }) };
1682
+ }
1545
1683
  function isJudgeCheck(check) {
1546
1684
  return "judge" in check;
1547
1685
  }
@@ -1595,7 +1733,7 @@ function checksIn(root, log, tag) {
1595
1733
  const hostChecks = loaded.customs.filter((def) => def.runWhere === "host" && def.level !== "off" && def.declaresEmit !== null);
1596
1734
  const checks = [];
1597
1735
  for (const def of hostChecks) {
1598
- if (def.judge !== void 0) checks.push({ name: def.declaresEmit, judge: { brief: def.judge.brief, text: readBrief(root, def.judge.brief) } });
1736
+ if (def.judge !== void 0) checks.push({ name: def.declaresEmit, judge: { brief: def.judge.brief, text: readBrief(root, def.judge.brief), subjects: def.judge.subjects } });
1599
1737
  else if (def.command !== void 0) checks.push({ name: def.declaresEmit, command: def.command });
1600
1738
  }
1601
1739
  return checks;
@@ -1665,7 +1803,11 @@ function makeRecorder(input) {
1665
1803
  "/v1/emitted-results",
1666
1804
  config.apiKey,
1667
1805
  config.appId,
1668
- { schemaVersion: 1, emit: { clientEmitId, name, result, ...body === void 0 ? {} : { body }, ...opts.link === void 0 ? {} : { link: opts.link }, emittedAt: now().toISOString(), item: { number: Number(job.item) } } },
1806
+ {
1807
+ schemaVersion: 1,
1808
+ // A field left undefined is not sent: the request is JSON.
1809
+ emit: { clientEmitId, name, result, body, link: opts.link, items: opts.items, emittedAt: now().toISOString(), item: { number: Number(job.item) } }
1810
+ },
1669
1811
  deps.fetchImpl,
1670
1812
  void 0,
1671
1813
  signer
@@ -1715,7 +1857,7 @@ async function runHostChecks(input) {
1715
1857
  log.line(`${tag} checks started (${checks.map((check) => check.name).join(", ")})`);
1716
1858
  const startedMs = now().getTime();
1717
1859
  const recorder = makeRecorder({ config, job, deps });
1718
- const record = (check, result, sentence, keyTail = "") => recorder(check.name, result, sentence, { keyTail });
1860
+ const record = (check, result, sentence, keyTail = "", items) => recorder(check.name, result, sentence, { keyTail, items });
1719
1861
  const failAll = async (reason, keyTail = "") => {
1720
1862
  log.error(`${tag} checks failed: ${reason}${keyTail}`);
1721
1863
  for (const check of checks) await record(check, "fail", reason, keyTail);
@@ -1884,7 +2026,9 @@ async function runHostChecks(input) {
1884
2026
  continue;
1885
2027
  }
1886
2028
  const checkStartedMs = now().getTime();
1887
- const running = ready.exec(["/bin/sh", "-c", check.command], CHECKOUT_DIR, { logPath: join(jobDir, "checks", `${check.name}.log`) });
2029
+ const logPath = join(jobDir, "checks", `${check.name}.log`);
2030
+ await ready.exec(["rm", "-f", CHECK_ITEMS_FILE], CHECKOUT_DIR, { logPath });
2031
+ const running = ready.exec(["/bin/sh", "-c", check.command], CHECKOUT_DIR, { logPath });
1888
2032
  running.catch(() => void 0);
1889
2033
  const timer = timeoutAfter(limitMinutes * 6e4, deps.sleep);
1890
2034
  let outcome;
@@ -1904,14 +2048,25 @@ async function runHostChecks(input) {
1904
2048
  restartTimer.cancel();
1905
2049
  continue;
1906
2050
  }
1907
- if (outcome.status === 0) {
2051
+ const read = await ready.exec(["head", "-c", String(MAX_CHECK_ITEMS_BYTES + 1), CHECK_ITEMS_FILE], CHECKOUT_DIR);
2052
+ const items = commandItems(read, (text2) => scrubReason(text2, config) ?? text2);
2053
+ if (!items.ok) {
2054
+ await record(check, "fail", items.reason);
2055
+ continue;
2056
+ }
2057
+ const failing = items.items === void 0 ? void 0 : itemsSentence(items.items, { one: "check item", many: "check items" });
2058
+ if (outcome.status === 0 && failing === void 0) {
1908
2059
  log.line(`${tag} check ${check.name} ran (${formatDuration(now().getTime() - checkStartedMs)})`);
1909
- await record(check, "pass");
2060
+ await record(check, "pass", void 0, "", items.items);
2061
+ continue;
2062
+ }
2063
+ if (outcome.status === 0) {
2064
+ await record(check, "fail", failing, "", items.items);
1910
2065
  continue;
1911
2066
  }
1912
2067
  const last = lastLine(outcome.stderr) ?? lastLine(outcome.stdout);
1913
2068
  const scrubbed = last === void 0 ? void 0 : scrubReason(last, config);
1914
- await record(check, "fail", `exited ${outcome.status}${scrubbed === void 0 ? " with no output" : `: ${cutReasonLine(scrubbed, 140)}`}`);
2069
+ await record(check, "fail", `exited ${outcome.status}${scrubbed === void 0 ? " with no output" : `: ${cutReasonLine(scrubbed, 140)}`}`, "", items.items);
1915
2070
  }
1916
2071
  } catch (err) {
1917
2072
  log.error(`${tag} checks stopped: ${describeCallFailure(err)}`);
@@ -1934,9 +2089,6 @@ async function runHostChecks(input) {
1934
2089
  }
1935
2090
 
1936
2091
  // src/runner/judge-verdict.ts
1937
- function mechanicalFailures(criteria, citations) {
1938
- return criteria.filter((criterion) => criterion.proof === null && (citations[criterion.id]?.length ?? 0) === 0).map((criterion) => criterion.id);
1939
- }
1940
2092
  function firstLine2(text2) {
1941
2093
  return text2.trim().split(/\r\n|\r|\n/)[0]?.trim() ?? "";
1942
2094
  }
@@ -1952,26 +2104,68 @@ function parseOutput(stdout) {
1952
2104
  }
1953
2105
  var RESULT_LINE = /^result:\s*(pass|fail)\s*$/i;
1954
2106
  var UNMET_LINE = /^unmet:\s*(\S+)\s*$/i;
1955
- function decideVerdict(input) {
1956
- const { run, criteria } = input;
1957
- if (run.kind === "timeout") return { result: "fail", reason: `the judge ran out of time after ${input.minutes}`, limitKey: true };
2107
+ function readJudgeReply(run, minutes) {
2108
+ if (run.kind === "timeout") return { kind: "none", reason: `the judge ran out of time after ${minutes}`, limitKey: true };
1958
2109
  const output = parseOutput(run.stdout);
1959
2110
  if (output === void 0) {
1960
2111
  const said = firstLine2(run.stderr);
1961
- return { result: "fail", reason: `the judge exited ${run.status} and printed no result${said === "" ? "" : `: ${said}`}` };
2112
+ return { kind: "none", reason: `the judge exited ${run.status} and printed no result${said === "" ? "" : `: ${said}`}` };
1962
2113
  }
1963
- if (output.subtype === "error_max_turns") return { result: "fail", reason: `the judge reached the turn limit of ${JUDGE_MAX_TURNS} turns before it gave a result` };
2114
+ if (output.subtype === "error_max_turns") return { kind: "none", reason: `the judge reached the turn limit of ${JUDGE_MAX_TURNS} turns before it gave a result` };
1964
2115
  const lines = output.result.split(/\r\n|\r|\n/).map((line) => line.trim()).filter((line) => line !== "");
1965
2116
  const verdict = RESULT_LINE.exec(lines.at(-1) ?? "")?.[1]?.toLowerCase();
1966
- if (verdict === void 0) return { result: "fail", reason: "the judge gave no `result:` line" };
1967
- const named = new Set(lines.flatMap((line) => UNMET_LINE.exec(line)?.[1] ?? []));
1968
- const mechanical = new Set(mechanicalFailures(criteria, input.citations));
1969
- const unmet = criteria.map((criterion) => criterion.id).filter((id) => named.has(id) || mechanical.has(id));
1970
- if (verdict === "pass" && unmet.length === 0) return { result: "pass" };
1971
- const ruled = unmet.filter((id) => mechanical.has(id));
1972
- const rule = ruled.length === 0 ? "" : `: the mechanical rule found no proof annotation and no citing test for ${ruled.join(", ")}`;
1973
- if (unmet.length === 0) return { result: "fail", reason: "the judge failed the change and named no criterion" };
1974
- return { result: "fail", reason: `${unmet.length} of ${criteria.length} criteria not satisfied, first ${unmet[0]}${rule}` };
2117
+ if (verdict === void 0) return { kind: "none", reason: "the judge gave no `result:` line" };
2118
+ return { kind: "answered", verdict, unmet: new Set(lines.flatMap((line) => UNMET_LINE.exec(line)?.[1] ?? [])) };
2119
+ }
2120
+
2121
+ // src/runner/judge-report.ts
2122
+ function escape(text2) {
2123
+ return text2.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2124
+ }
2125
+ var RESULT_LABEL = { pass: "satisfied", fail: "not satisfied", skipped: "not judged" };
2126
+ function refsCell(item) {
2127
+ return (item.refs ?? []).map((ref) => {
2128
+ const label = escape(ref.line === void 0 ? ref.path : `${ref.path}:${ref.line}`);
2129
+ return ref.href === void 0 ? `<code>${label}</code>` : `<a href="${escape(ref.href)}"><code>${label}</code></a>`;
2130
+ }).join("<br>");
2131
+ }
2132
+ function renderJudgeReport(input) {
2133
+ const count = (result) => input.items.filter((item) => item.result === result).length;
2134
+ const summary = `${count("pass")} satisfied, ${count("fail")} not satisfied, ${count("skipped")} not judged`;
2135
+ const rows = input.items.map(
2136
+ (item) => `<tr class="${item.result}"><td><code>${escape(item.id)}</code></td><td>${RESULT_LABEL[item.result]}</td><td>${escape(item.reason ?? "")}</td><td>${refsCell(item)}</td></tr>`
2137
+ ).join("\n");
2138
+ return `<!doctype html>
2139
+ <html lang="en">
2140
+ <head>
2141
+ <meta charset="utf-8">
2142
+ <meta name="viewport" content="width=device-width, initial-scale=1">
2143
+ <title>${escape(input.check)}</title>
2144
+ <style>
2145
+ :root { color-scheme: light dark; --fail: #b42318; --pass: #067647; --muted: #667085; }
2146
+ body { font: 14px/1.5 system-ui, sans-serif; margin: 24px; max-width: 1100px; }
2147
+ table { border-collapse: collapse; width: 100%; }
2148
+ th, td { text-align: left; vertical-align: top; padding: 6px 10px; border-bottom: 1px solid color-mix(in srgb, currentColor 15%, transparent); }
2149
+ tr.fail td:nth-child(2) { color: var(--fail); font-weight: 600; }
2150
+ tr.pass td:nth-child(2) { color: var(--pass); }
2151
+ tr.skipped td:nth-child(2) { color: var(--muted); }
2152
+ .meta { color: var(--muted); }
2153
+ </style>
2154
+ ${input.extraHead ?? ""}
2155
+ </head>
2156
+ <body>
2157
+ <h1>${escape(input.check)}: ${input.result}</h1>
2158
+ <p>${escape(summary)}.${input.reason === void 0 ? "" : ` ${escape(input.reason)}.`}</p>
2159
+ <p class="meta">Judged at commit <code>${escape(input.commit)}</code>.</p>
2160
+ <table>
2161
+ <thead><tr><th>Item</th><th>Result</th><th>Reason</th><th>Tests</th></tr></thead>
2162
+ <tbody>
2163
+ ${rows}
2164
+ </tbody>
2165
+ </table>
2166
+ </body>
2167
+ </html>
2168
+ `;
1975
2169
  }
1976
2170
 
1977
2171
  // src/runner/judge.ts
@@ -2029,6 +2223,14 @@ function criteriaOf(workItem) {
2029
2223
  }
2030
2224
  return items.length === 0 ? void 0 : items;
2031
2225
  }
2226
+ function hostItemsOf(workItem, name) {
2227
+ const checks = Object(workItem).checks;
2228
+ if (!Array.isArray(checks)) return [];
2229
+ const check = checks.find((entry) => Object(entry).name === name);
2230
+ if (check?.source !== "host") return [];
2231
+ const parsed = CheckItemsSchema.safeParse(check.items);
2232
+ return parsed.success ? parsed.data : [];
2233
+ }
2032
2234
  function environmentFor(input) {
2033
2235
  const { config, job, jobDir, deps } = input;
2034
2236
  if (deps.judgeEnvironment !== void 0) return deps.judgeEnvironment;
@@ -2105,7 +2307,8 @@ async function runJudge(input) {
2105
2307
  if (environment === void 0) return await fail(check, `${NO_CLAUDE}: this build ran outside a container or microVM`);
2106
2308
  const expected = (job.container ?? job.microvm)?.judgeProgram;
2107
2309
  if (expected === void 0) return await fail(check, NO_CLAUDE);
2108
- if (check.judge.text === void 0) return await fail(check, `the brief ${check.judge.brief} is not in the definitions repository`);
2310
+ const brief = check.judge.text;
2311
+ if (brief === void 0) return await fail(check, `the brief ${check.judge.brief} is not in the definitions repository`);
2109
2312
  if (tokens === void 0) return await fail(check, "the build's repository tokens are no longer held, so the branch cannot be fetched");
2110
2313
  const credential = claudeCredential(runtime.hostEnv);
2111
2314
  if (credential === void 0) return await fail(check, "the host holds no Claude credential for the judge");
@@ -2125,17 +2328,34 @@ async function runJudge(input) {
2125
2328
  if (read.kind === "unreadable") return await fail(check, `the branch ${branch} could not be read: ${read.reason}`);
2126
2329
  const head = read.sha;
2127
2330
  let criteria;
2128
- try {
2129
- criteria = criteriaOf(await readWorkItem(config, job.item, deps.fetchImpl));
2130
- } catch (err) {
2131
- return await fail(check, `the item's criteria could not be read: ${describeCallFailure(err)}`);
2331
+ let previous = [];
2332
+ if (check.judge.subjects === "criteria") {
2333
+ let workItem;
2334
+ try {
2335
+ workItem = await readWorkItem(config, job.item, deps.fetchImpl);
2336
+ } catch (err) {
2337
+ return await fail(check, `the item's criteria could not be read: ${describeCallFailure(err)}`);
2338
+ }
2339
+ criteria = criteriaOf(workItem);
2340
+ if (criteria === void 0) return await fail(check, "no criteria are recorded for the item");
2341
+ previous = hostItemsOf(workItem, check.name);
2132
2342
  }
2133
- if (criteria === void 0) return await fail(check, "no criteria are recorded for the item");
2134
2343
  const branchDir = join(scratch, "branch");
2135
2344
  const fetched = await (deps.fetchCommit ?? fetchCommit)({ remote, dir: branchDir, commit: head, token: readToken });
2136
2345
  if (!fetched.ok) return await fail(check, `the build's tests could not be fetched: ${fetched.reason}`);
2137
2346
  const finder = await loadCitationFinder();
2138
- const citations = finder.buildCitations({ ids: criteria.map((criterion) => criterion.id), files: testFilesIn(branchDir, finder.TEST_FILE), repository: repo, sha: head });
2347
+ const list = criteria ?? [];
2348
+ const testFiles = testFilesIn(branchDir, finder.TEST_FILE);
2349
+ const citations = finder.buildCitations({ ids: list.map((criterion) => criterion.id), files: testFiles, repository: repo, sha: head });
2350
+ const testFileText = new Map(testFiles.map((file) => [file.path, file.contents]));
2351
+ const inputs = new Map(list.map((criterion) => [criterion.id, criterionInputs({ brief, criterion, citations, testFiles: testFileText })]));
2352
+ const plan = planCriteria({ criteria: list, citations, inputs, previous });
2353
+ if (plan.toJudge.length < list.length) {
2354
+ log.line(`${tag} judge: ${plan.toJudge.length} of ${list.length} criteria to judge, ${list.length - plan.toJudge.length} settled without the judge`);
2355
+ }
2356
+ if (criteria !== void 0 && plan.toJudge.length === 0) {
2357
+ return await finish(check, { criteria, items: plan.settled, citations, head, branch, finder, judgeReport: void 0 });
2358
+ }
2139
2359
  let diff = "";
2140
2360
  const startCommit = (job.container ?? job.microvm)?.startCommit;
2141
2361
  if (startCommit !== void 0) {
@@ -2147,19 +2367,26 @@ async function runJudge(input) {
2147
2367
  [the diff was cut at ${MAX_DIFF_BYTES} bytes]
2148
2368
  ` : bytes.toString("utf8");
2149
2369
  }
2150
- await environment.prepare({
2151
- "criteria.json": `${JSON.stringify({ criteria }, null, 2)}
2152
- `,
2153
- "citations.json": `${JSON.stringify(citations, null, 2)}
2370
+ await environment.prepare(
2371
+ {
2372
+ ...criteria === void 0 ? {} : {
2373
+ // Only the criteria the judge is asked about. The others are settled.
2374
+ "criteria.json": `${JSON.stringify({ criteria: plan.toJudge }, null, 2)}
2154
2375
  `,
2155
- "diff.patch": diff
2156
- }, branchDir);
2376
+ "citations.json": `${JSON.stringify(citations, null, 2)}
2377
+ `
2378
+ },
2379
+ "diff.patch": diff
2380
+ },
2381
+ branchDir
2382
+ );
2157
2383
  const limitMinutes = config.checks?.timeLimitMinutes ?? DEFAULT_CHECK_TIME_LIMIT_MINUTES;
2158
2384
  const minutes = `${limitMinutes} ${limitMinutes === 1 ? "minute" : "minutes"}`;
2159
2385
  const clear = async () => {
2160
2386
  await environment.clear?.().catch((err) => log.error(`${tag} what the judge left in its folder could not be removed: ${describeCallFailure(err)}`));
2161
2387
  };
2162
- const running = environment.run(judgeCommand({ brief: check.judge.text, credential }), { logPath: join(jobDir, "judge.log") });
2388
+ const prompt = withAnswerProtocol(brief, check.judge.subjects);
2389
+ const running = environment.run(judgeCommand({ brief: prompt, credential }), { logPath: join(jobDir, "judge.log") });
2163
2390
  running.catch(() => void 0);
2164
2391
  const timer = timeoutAfter(limitMinutes * 6e4, deps.sleep);
2165
2392
  let outcome;
@@ -2172,27 +2399,86 @@ async function runJudge(input) {
2172
2399
  timer.cancel();
2173
2400
  }
2174
2401
  let report;
2175
- const readReport2 = async () => {
2402
+ let answerText;
2403
+ const readOutputs = async () => {
2176
2404
  try {
2177
2405
  report = await environment.readReport();
2178
2406
  } catch (err) {
2179
2407
  log.error(`${tag} the judge's report could not be read: ${describeCallFailure(err)}`);
2180
2408
  }
2409
+ try {
2410
+ answerText = await environment.readItems();
2411
+ } catch (err) {
2412
+ log.error(`${tag} the judge's items could not be read: ${describeCallFailure(err)}`);
2413
+ }
2181
2414
  };
2182
2415
  if (outcome === "timeout") {
2183
- await readReport2();
2416
+ await readOutputs();
2184
2417
  await environment.stop().catch((err) => log.error(`${tag} the judge could not be stopped after its time ran out: ${describeCallFailure(err)}`));
2418
+ } else {
2419
+ await readOutputs();
2185
2420
  }
2186
- const decision = decideVerdict({
2187
- run: outcome === "timeout" ? { kind: "timeout" } : { kind: "exited", status: outcome.status, stdout: outcome.stdout, stderr: outcome.stderr },
2188
- criteria,
2189
- citations,
2190
- minutes
2191
- });
2192
- if (outcome !== "timeout") await readReport2();
2193
2421
  await clear();
2194
- const link = report === void 0 ? void 0 : await uploadReport(check, report, { branch, head });
2195
- await record(check.name, decision.result, decision.reason, { keyTail: decision.limitKey === true ? LIMIT_KEY_TAIL2 : "", link });
2422
+ const run = outcome === "timeout" ? { kind: "timeout" } : { kind: "exited", status: outcome.status, stdout: outcome.stdout, stderr: outcome.stderr };
2423
+ const reply = readJudgeReply(run, minutes);
2424
+ const answer = parseJudgeAnswer(answerText);
2425
+ if (criteria === void 0) return await finishWhole(check, { reply, answer, report, head, branch });
2426
+ const judged = (results2) => [
2427
+ ...plan.settled,
2428
+ ...judgedItems({ toJudge: plan.toJudge, results: results2, citations, inputs })
2429
+ ];
2430
+ if (answer?.ok === true) {
2431
+ const results2 = new Map(answer.answer.items.map((entry) => [entry.id, entry]));
2432
+ return await finish(check, { criteria, items: judged(results2), citations, head, branch, finder, judgeReport: void 0 });
2433
+ }
2434
+ if (reply.kind === "none") {
2435
+ return await finish(check, { criteria, items: judged(/* @__PURE__ */ new Map()), citations, head, branch, finder, judgeReport: report, reason: reply.reason, limitKey: reply.limitKey === true });
2436
+ }
2437
+ if (answer !== void 0) {
2438
+ return await finish(check, { criteria, items: judged(/* @__PURE__ */ new Map()), citations, head, branch, finder, judgeReport: report, reason: answer.reason });
2439
+ }
2440
+ const failsAll = reply.verdict === "fail" && reply.unmet.size === 0;
2441
+ const results = new Map(plan.toJudge.map((criterion) => [criterion.id, { result: failsAll || reply.unmet.has(criterion.id) ? "fail" : "pass" }]));
2442
+ return await finish(check, { criteria, items: judged(results), citations, head, branch, finder, judgeReport: report });
2443
+ }
2444
+ async function finish(check, at) {
2445
+ const items = inCriteriaOrder(at.items, at.criteria);
2446
+ const failing = itemsSentence(items, { one: "criterion", many: "criteria" });
2447
+ const result = failing === void 0 ? "pass" : "fail";
2448
+ const reason = at.reason ?? failing;
2449
+ const report = at.judgeReport !== void 0 && at.judgeReport !== "" ? at.judgeReport : renderJudgeReport({ check: check.name, result, reason, items, commit: at.head, extraHead: at.finder.renderBlock(at.citations) });
2450
+ const link = await uploadReport(check, report, { branch: at.branch, head: at.head });
2451
+ await record(check.name, result, reason, { keyTail: at.limitKey === true ? LIMIT_KEY_TAIL2 : "", link, items: sendable(items) });
2452
+ }
2453
+ async function finishWhole(check, at) {
2454
+ const where = { head: at.head, branch: at.branch };
2455
+ if (at.answer?.ok === true) {
2456
+ const items = at.answer.answer.items.map((entry) => {
2457
+ const reason2 = oneLineReason(entry.reason);
2458
+ return { id: entry.id, result: entry.result, ...reason2 === void 0 ? {} : { reason: reason2 } };
2459
+ });
2460
+ const failing = itemsSentence(items, { one: "check item", many: "check items" });
2461
+ const said = at.answer.answer.result ?? (at.reply.kind === "answered" ? at.reply.verdict : void 0);
2462
+ const result = said === "pass" && failing === void 0 ? "pass" : "fail";
2463
+ const reason = failing ?? (said === "fail" ? "the judge failed the change" : said === void 0 ? at.reply.kind === "none" ? at.reply.reason : "the judge gave no result" : void 0);
2464
+ const report = renderJudgeReport({ check: check.name, result, reason, items, commit: at.head });
2465
+ return await recordWhole(check, { ...where, result, reason, items, report });
2466
+ }
2467
+ if (at.reply.kind === "none") return await recordWhole(check, { ...where, result: "fail", reason: at.reply.reason, report: at.report, limitKey: at.reply.limitKey === true });
2468
+ if (at.answer !== void 0) return await recordWhole(check, { ...where, result: "fail", reason: at.answer.reason, report: at.report });
2469
+ const failed = at.reply.verdict === "fail";
2470
+ return await recordWhole(check, { ...where, result: at.reply.verdict, reason: failed ? "the judge failed the change" : void 0, report: at.report });
2471
+ }
2472
+ async function recordWhole(check, at) {
2473
+ const link = at.report === void 0 ? void 0 : await uploadReport(check, at.report, { branch: at.branch, head: at.head });
2474
+ await record(check.name, at.result, at.reason, { keyTail: at.limitKey === true ? LIMIT_KEY_TAIL2 : "", link, items: sendable(at.items) });
2475
+ }
2476
+ function sendable(items) {
2477
+ if (items === void 0) return void 0;
2478
+ const parsed = CheckItemsSchema.safeParse(items);
2479
+ if (parsed.success) return parsed.data;
2480
+ log.error(`${tag} the judge's check items were left off the result: ${parsed.error.issues.map((issue) => issue.message).join("; ")}`);
2481
+ return void 0;
2196
2482
  }
2197
2483
  async function uploadReport(check, report, at) {
2198
2484
  const bytes = Buffer.from(report, "utf8");