@bjoernrochel/sevro 0.1.0-rc.8 → 1.0.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 (228) hide show
  1. package/CONTRIBUTING.md +22 -6
  2. package/README.md +5 -1
  3. package/docs/README.md +19 -15
  4. package/docs/architecture.md +138 -17
  5. package/docs/assets/README.md +5 -4
  6. package/docs/builtin-graders-v1.md +9 -2
  7. package/docs/codex-host.md +155 -57
  8. package/docs/contracts.md +2 -1
  9. package/docs/creating-cases.md +9 -2
  10. package/docs/development-cli.md +187 -19
  11. package/docs/documentation-quality.md +12 -2
  12. package/docs/evaluation-workflow.md +110 -0
  13. package/docs/extension-protocol-v1.md +26 -3
  14. package/docs/getting-started.md +23 -2
  15. package/docs/guide-evaluation.md +33 -1
  16. package/docs/identity-v1.md +25 -2
  17. package/docs/installing.md +11 -11
  18. package/docs/native-controls.md +76 -5
  19. package/docs/native-hosts.md +196 -7
  20. package/docs/reading-results.md +102 -0
  21. package/docs/recipes.md +489 -0
  22. package/docs/releases.md +31 -12
  23. package/docs/report-v1.md +29 -1
  24. package/docs/results-v1.md +122 -4
  25. package/docs/run-settings-v1.md +147 -0
  26. package/docs/runtime-v1.md +255 -22
  27. package/docs/troubleshooting.md +12 -12
  28. package/docs/typescript-quality.md +89 -26
  29. package/examples/recipes/candidate.ts +26 -0
  30. package/examples/recipes/cases/coding-change.json +21 -0
  31. package/examples/recipes/cases/host-tools.json +21 -0
  32. package/examples/recipes/cases/mise.json +21 -0
  33. package/examples/recipes/cases/node.json +21 -0
  34. package/examples/recipes/cases/python-uv.json +21 -0
  35. package/examples/recipes/cases/semantic.json +17 -0
  36. package/examples/recipes/coding-change/check.ts +23 -0
  37. package/examples/recipes/extension-artifacts.ts +65 -0
  38. package/examples/recipes/extension-methods.ts +174 -0
  39. package/examples/recipes/extension.ts +37 -0
  40. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  41. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  42. package/examples/recipes/repository-skill/README.md +3 -0
  43. package/examples/recipes/run-inputs.ts +188 -0
  44. package/examples/recipes/run-options.ts +131 -0
  45. package/examples/recipes/run.ts +63 -0
  46. package/examples/recipes/runtime/empty.json +1 -0
  47. package/examples/recipes/runtime/goals.json +1 -0
  48. package/examples/recipes/runtime/mise.json +6 -0
  49. package/examples/recipes/runtime/path.json +1 -0
  50. package/examples/recipes/runtime/plugins.json +4 -0
  51. package/examples/recipes/runtime/python-uv.json +10 -0
  52. package/examples/recipes/semantic-judge.ts +29 -0
  53. package/examples/templates/coding/adapter.ts +17 -0
  54. package/examples/templates/coding/case.json +15 -0
  55. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  56. package/examples/templates/repository-skill/extension-data.ts +64 -0
  57. package/examples/templates/repository-skill/extension.ts +31 -0
  58. package/examples/templates/response/adapter.ts +12 -0
  59. package/examples/templates/response/case.json +13 -0
  60. package/package.json +11 -5
  61. package/schemas/report-v1.schema.json +9 -0
  62. package/schemas/run-evidence-v1.schema.json +2 -1
  63. package/schemas/runtime-v1.schema.json +43 -7
  64. package/src/advisory.ts +2 -2
  65. package/src/candidate-artifacts.ts +42 -11
  66. package/src/candidate-codex-state.ts +185 -0
  67. package/src/case-validation.ts +73 -50
  68. package/src/cli/cancellation.ts +20 -0
  69. package/src/cli/command.ts +87 -0
  70. package/src/cli/completion.ts +15 -0
  71. package/src/cli/demo.ts +60 -0
  72. package/src/cli/evaluation.ts +55 -0
  73. package/src/cli/extension.ts +79 -0
  74. package/src/cli/help.ts +198 -0
  75. package/src/cli/hosts.ts +55 -0
  76. package/src/cli/init-settings.ts +30 -0
  77. package/src/cli/init.ts +111 -0
  78. package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
  79. package/src/cli/loading.ts +151 -0
  80. package/src/cli/preparation.ts +154 -0
  81. package/src/cli/presentation.ts +93 -0
  82. package/src/cli/progress.ts +6 -0
  83. package/src/cli/run-manifest.ts +128 -0
  84. package/src/cli/run-options.ts +403 -0
  85. package/src/cli/run-overrides.ts +69 -0
  86. package/src/cli/settings.ts +154 -0
  87. package/src/cli/summary.ts +43 -0
  88. package/src/cli.ts +13 -591
  89. package/src/contract-types.ts +94 -0
  90. package/src/engine.ts +9 -5
  91. package/src/evaluation/advisory.ts +150 -0
  92. package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
  93. package/src/evaluation/collection.ts +88 -0
  94. package/src/evaluation/context.ts +10 -0
  95. package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
  96. package/src/evaluation/grading.ts +235 -0
  97. package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
  98. package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
  99. package/src/evaluation/progress.ts +19 -0
  100. package/src/evaluation/resources.ts +241 -0
  101. package/src/evaluation/run.ts +206 -0
  102. package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +77 -87
  103. package/src/evaluation/trial.ts +800 -0
  104. package/src/{evaluation-types.ts → evaluation/types.ts} +79 -29
  105. package/src/evidence/host-results.ts +85 -0
  106. package/src/evidence/retained-artifacts.ts +18 -0
  107. package/src/evidence/trial-writer.ts +203 -0
  108. package/src/evidence/usage.ts +17 -0
  109. package/src/extension-client.ts +34 -96
  110. package/src/extension-session.ts +24 -48
  111. package/src/{advisory-fixture.ts → fixtures/advisory.ts} +12 -72
  112. package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
  113. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  114. package/src/fixtures/create.ts +183 -0
  115. package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
  116. package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
  117. package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
  118. package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
  119. package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
  120. package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
  121. package/src/{workspace-state.ts → fixtures/workspace-state.ts} +5 -5
  122. package/src/generated/cli-result.d.cts +2 -1
  123. package/src/generated/extension.d.cts +2 -1
  124. package/src/generated/report.cjs +1 -1
  125. package/src/generated/report.d.cts +2 -1
  126. package/src/generated/run-evidence.cjs +1 -1
  127. package/src/generated/run-evidence.d.cts +2 -1
  128. package/src/generated/runtime.cjs +1 -1
  129. package/src/generated/runtime.d.cts +2 -1
  130. package/src/git.ts +93 -0
  131. package/src/graders/git-head.ts +29 -84
  132. package/src/graders/shell.ts +50 -68
  133. package/src/host-diagnostic-redaction.ts +52 -0
  134. package/src/host-diagnostics.ts +331 -0
  135. package/src/hosts/claude-continuation.ts +57 -40
  136. package/src/hosts/claude-credential.ts +160 -63
  137. package/src/hosts/claude-events.ts +34 -18
  138. package/src/hosts/claude-keychain.ts +78 -21
  139. package/src/hosts/claude-login-recovery.ts +85 -0
  140. package/src/hosts/claude-login-refresh.ts +178 -0
  141. package/src/hosts/claude-login-store.ts +162 -0
  142. package/src/hosts/claude-login.ts +107 -0
  143. package/src/hosts/claude-native-goal.ts +15 -12
  144. package/src/hosts/claude-process.ts +10 -50
  145. package/src/hosts/claude-refresh-lock.ts +81 -0
  146. package/src/hosts/claude-repository-invocation.ts +9 -34
  147. package/src/hosts/claude-settings.ts +46 -9
  148. package/src/hosts/claude-skill-activations.ts +169 -0
  149. package/src/hosts/claude-state.ts +28 -15
  150. package/src/hosts/claude-tool-calls.ts +13 -10
  151. package/src/hosts/claude.ts +121 -81
  152. package/src/hosts/codex-app-server.ts +78 -52
  153. package/src/hosts/codex-configuration.ts +5 -10
  154. package/src/hosts/codex-exec-controls.ts +516 -0
  155. package/src/hosts/codex-marketplace.ts +12 -10
  156. package/src/hosts/codex-native-calls.ts +15 -122
  157. package/src/hosts/codex-process.ts +10 -183
  158. package/src/hosts/codex-profile.ts +36 -21
  159. package/src/hosts/codex-session-files.ts +1 -9
  160. package/src/hosts/codex-state.ts +134 -18
  161. package/src/hosts/codex.ts +210 -183
  162. package/src/hosts/isolation-common.ts +8 -11
  163. package/src/hosts/isolation-roots.ts +2 -2
  164. package/src/hosts/linux-sandbox.ts +15 -2
  165. package/src/hosts/mac-sandbox.ts +87 -18
  166. package/src/hosts/native-controls.ts +72 -22
  167. package/src/hosts/native-request.ts +55 -0
  168. package/src/hosts/process-lifecycle.ts +216 -48
  169. package/src/hosts/protected-worktrees.ts +1 -1
  170. package/src/hosts/skill-hook-catalog.ts +131 -0
  171. package/src/hosts/skill-hook-handler.ts +374 -0
  172. package/src/hosts/skill-hooks.ts +577 -0
  173. package/src/instrumentation.ts +7 -8
  174. package/src/native-transcript-state.ts +22 -3
  175. package/src/native-transcript-view.ts +52 -13
  176. package/src/native-transcripts.ts +26 -11
  177. package/src/paths.ts +22 -0
  178. package/src/process-lifecycle.ts +150 -0
  179. package/src/provenance.ts +27 -7
  180. package/src/prune.ts +300 -0
  181. package/src/report.ts +63 -36
  182. package/src/resolved-case.ts +1 -1
  183. package/src/results-lock.ts +42 -0
  184. package/src/results.ts +14 -18
  185. package/src/{runtime-config.ts → runtime/config.ts} +160 -46
  186. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  187. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  188. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  189. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
  190. package/src/{runtime-hooks.ts → runtime/hooks.ts} +32 -22
  191. package/src/{runtime-paths.ts → runtime/paths.ts} +5 -19
  192. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  193. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  194. package/src/runtime/shared-cache.ts +130 -0
  195. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  196. package/src/schema-types.ts +73 -161
  197. package/src/shell.ts +4 -0
  198. package/src/transcript-retention.ts +146 -0
  199. package/src/value-guards.ts +22 -0
  200. package/docs/assets/sevro-logo.png +0 -0
  201. package/docs/assets/sevro-symbol.svg +0 -1
  202. package/docs/assets/sevro-wordmark.svg +0 -1
  203. package/docs/cache-ownership-validation.md +0 -49
  204. package/docs/claude-continuation-validation.md +0 -47
  205. package/docs/concurrency-validation.md +0 -86
  206. package/docs/configuration-root-validation.md +0 -70
  207. package/docs/engine-slice.md +0 -114
  208. package/docs/evidence/runtime-startup-30/README.md +0 -185
  209. package/docs/evidence/runtime-startup-30/after.json +0 -319
  210. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  211. package/docs/evidence/runtime-startup-30/before.json +0 -403
  212. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  213. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  214. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  215. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  216. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  217. package/docs/evidence.md +0 -17
  218. package/docs/fixture-cleanup-validation.md +0 -73
  219. package/docs/guide-verification.json +0 -239
  220. package/src/evaluation-context.ts +0 -259
  221. package/src/evaluation-fixture.ts +0 -302
  222. package/src/evaluation-run.ts +0 -200
  223. package/src/evaluation-trial.ts +0 -1016
  224. package/src/hosts/claude-nested-skills.ts +0 -272
  225. package/src/hosts/codex-command-output.ts +0 -510
  226. package/src/hosts/codex-executor-command.ts +0 -291
  227. package/src/hosts/codex-skill-reads.ts +0 -783
  228. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
@@ -1,1016 +0,0 @@
1
- import { rm, writeFile } from "node:fs/promises";
2
- import {
3
- allocateCandidateRuntime,
4
- candidateArtifactRoots,
5
- releaseCandidateRuntime,
6
- type CandidateArtifactRoots,
7
- } from "./candidate-artifacts";
8
- import { join } from "node:path";
9
- import { pathToFileURL } from "node:url";
10
- import {
11
- advisoryPrompt,
12
- parseAdvisoryAssessment,
13
- type AdvisoryAssessment,
14
- } from "./advisory";
15
- import {
16
- advisoryBaseRevision,
17
- buildBlindAdvisoryFixture,
18
- } from "./advisory-fixture";
19
- import {
20
- checkpointEvaluation,
21
- prepareTrialWorkspace,
22
- type EvaluationContext,
23
- } from "./evaluation-context";
24
- import {
25
- hostArtifacts,
26
- hostObservations,
27
- sha256,
28
- usage,
29
- verifyRetainedArtifacts,
30
- } from "./evaluation-fixture";
31
- import {
32
- gradingRuntimePolicy,
33
- scopedHostArtifacts,
34
- } from "./evaluation-grading-host";
35
- import { gradeSemanticChecks } from "./evaluation-semantic";
36
- import {
37
- type EvaluationOptions,
38
- type HostAdapter,
39
- type HostResult,
40
- type TrialSummary,
41
- } from "./evaluation-types";
42
- import {
43
- ExtensionSourceChangedError,
44
- type EvaluationResult,
45
- } from "./extension-session";
46
- import { clearFixtureContents } from "./fixture-cleanup";
47
- import {
48
- CodexProcessFailure,
49
- codexFailureMessage,
50
- } from "./hosts/codex-process";
51
- import { retainWorkspace, removeWorkspace } from "./workspace-state";
52
- import {
53
- assessGitHeadCheck,
54
- gitHeadRevision,
55
- gitHeadState,
56
- } from "./graders/git-head";
57
- import { gradeOutput } from "./graders/output";
58
- import { assessShellCheck, runShellCheck } from "./graders/shell";
59
- import { evaluationProtectedRoots } from "./hosts/isolation-roots";
60
- import {
61
- InstrumentationEvidenceError,
62
- snapshotInstrumentation,
63
- verifyAppliedInstrumentation,
64
- type InstrumentationRequest,
65
- } from "./instrumentation";
66
- import { materializeNativeTranscriptView } from "./native-transcript-view";
67
- import {
68
- applyTaskVerdictPolicy,
69
- assessTrial,
70
- type Assessment,
71
- type CheckOutcome,
72
- } from "./results";
73
- import type { RunEvidenceData } from "./schema-types";
74
- import { atomicWriteJson } from "./storage";
75
- import { scheduleTrials } from "./trial-scheduler";
76
-
77
- const MAX_FINAL_MESSAGE_BYTES = 8 * 1024 * 1024;
78
-
79
- interface TrialObservation {
80
- id: string;
81
- source: string;
82
- completeness: "complete" | "partial" | "unavailable";
83
- data: Record<string, unknown>;
84
- }
85
- function candidateMarketplaceRequest(context: EvaluationContext) {
86
- const marketplace = context.codexMarketplace;
87
- if (!marketplace) return {};
88
- return {
89
- codexMarketplace: {
90
- ...marketplace,
91
- artifactPaths: context.inlineArtifacts
92
- .filter((artifact) =>
93
- artifact.relativePath.startsWith(`${marketplace.artifactRoot}/`),
94
- )
95
- .map((artifact) => artifact.relativePath),
96
- },
97
- };
98
- }
99
-
100
- function candidatePluginDirectoriesRequest(context: EvaluationContext) {
101
- const directories = context.claudePluginDirs;
102
- if (!directories) return {};
103
- return {
104
- claudePluginDirs: {
105
- ...directories,
106
- artifactPaths: context.inlineArtifacts
107
- .filter((artifact) =>
108
- directories.artifactRoots.some((root) =>
109
- artifact.relativePath.startsWith(`${root}/`),
110
- ),
111
- )
112
- .map((artifact) => artifact.relativePath),
113
- },
114
- };
115
- }
116
-
117
- function candidateInvocationRequest(context: EvaluationContext) {
118
- if (context.invocation === undefined || context.invocationToken === null)
119
- return {};
120
- return {
121
- explicitSkillInvocation: {
122
- ...context.invocation,
123
- token: context.invocationToken,
124
- },
125
- };
126
- }
127
-
128
- function ensureNotCancelled(signal: AbortSignal | undefined): void {
129
- if (signal?.aborted) throw new Error("cancelled");
130
- }
131
-
132
- function hostFailureDiagnostic(execution: string, error: unknown) {
133
- if (execution === "cancelled")
134
- return { code: "sevro.run.cancelled", message: "run cancelled" };
135
- if (error instanceof InstrumentationEvidenceError)
136
- return { code: "sevro.instrumentation.mismatch", message: error.message };
137
- if (error instanceof CodexProcessFailure)
138
- return { code: "sevro.host.failed", message: error.message };
139
- return {
140
- code: "sevro.host.failed",
141
- message: "host execution did not complete",
142
- };
143
- }
144
-
145
- type AdvisoryReview = {
146
- status: "completed" | "failed" | "not_run";
147
- assessment: AdvisoryAssessment | null;
148
- usage: ReturnType<typeof usage>;
149
- rawResult: {
150
- source: string;
151
- path: string | null;
152
- sha256: string | null;
153
- };
154
- };
155
-
156
- function defaultAdvisoryReview(host: HostAdapter): AdvisoryReview {
157
- return {
158
- status: "not_run",
159
- assessment: null,
160
- usage: usage(null),
161
- rawResult: { source: host.id, path: null, sha256: null },
162
- };
163
- }
164
-
165
- function isTrialPersistenceFailure(error: unknown): boolean {
166
- return (
167
- error instanceof Error &&
168
- error.message.startsWith("trial persistence failed")
169
- );
170
- }
171
-
172
- function completeAdvisoryAssessment(response: HostResult): AdvisoryAssessment {
173
- if (
174
- response.executionFailed ||
175
- !response.complete ||
176
- response.finalMessage === null
177
- )
178
- throw new Error("advisory response is incomplete");
179
- return parseAdvisoryAssessment(response.finalMessage);
180
- }
181
-
182
- function extensionFinalMessageData(result: HostResult | null) {
183
- return result?.finalMessage == null ? {} : { text: result.finalMessage };
184
- }
185
-
186
- function extensionCheckResult(
187
- check: EvaluationResult["checks"][number],
188
- declared: Map<string, string>,
189
- ): CheckOutcome {
190
- const grader = declared.get(check.id);
191
- if (grader === undefined)
192
- throw new Error("extension returned an undeclared check");
193
- return {
194
- id: check.id,
195
- grader,
196
- status: check.status,
197
- ...(check.detail ? { detail: check.detail } : {}),
198
- evidenceRefs: check.evidenceRefs,
199
- };
200
- }
201
-
202
- function compareDeclaredChecks(
203
- left: CheckOutcome,
204
- right: CheckOutcome,
205
- order: Map<string, number>,
206
- ): number {
207
- const leftIndex = order.get(left.id);
208
- const rightIndex = order.get(right.id);
209
- // Match the stable NaN comparator fallback; assessment reports undeclared IDs.
210
- if (leftIndex === undefined || rightIndex === undefined) return 0;
211
- return leftIndex - rightIndex;
212
- }
213
-
214
- function selectedTrialTaskPolicy(
215
- options: EvaluationOptions,
216
- recommendation: "passed" | "failed" | "not_assessed" | null,
217
- ) {
218
- const id = options.extension?.session.identity.selectedTaskVerdictPolicy;
219
- return id ? { id, recommendation } : null;
220
- }
221
-
222
- class EvaluationTrial {
223
- private candidateTranscriptRoot?: string;
224
- private candidateRuntimeRoot?: string;
225
- private candidateArtifacts?: CandidateArtifactRoots;
226
- private diagnostic: { code: string; message: string } | undefined = undefined;
227
- private persisted: boolean = false;
228
- private gitHeadBase: string | null = null;
229
- private advisoryRevision: string | null = null;
230
- private hostResult: HostResult | null = null;
231
- private appliedInstrumentation: InstrumentationRequest[] = [];
232
- private additionalObservations: ReturnType<typeof hostObservations> = [];
233
- private producedArtifacts: ReturnType<typeof hostArtifacts> = [];
234
- private execution: "completed" | "failed" | "cancelled" | "not_run";
235
- private candidateDurationMs: number | null = null;
236
- private trialArtifactRefs: EvaluationContext["artifactRefs"] = [];
237
- private rawDigest: string | null = null;
238
- private completeness: "complete" | "partial" | "unavailable" = "unavailable";
239
- private observation: TrialObservation = {
240
- id: "sevro.observation.final-message",
241
- source: "",
242
- completeness: "unavailable",
243
- data: {},
244
- };
245
- private shellObservations: TrialObservation[] = [];
246
- private gitHeadObservations: TrialObservation[] = [];
247
- private semanticObservations: TrialObservation[] = [];
248
- private checks: CheckOutcome[] = [];
249
- private extensionMetrics: EvaluationResult["metrics"] = [];
250
- private domainOutcomes: NonNullable<EvaluationResult["domainOutcomes"]> = [];
251
- private taskPolicyRecommendation:
252
- "passed" | "failed" | "not_assessed" | null = null;
253
- private graderError: boolean = false;
254
- private assessment: Assessment = {
255
- execution: { status: "not_run" },
256
- grading: { status: "not_requested" },
257
- task: { verdict: "not_assessed" },
258
- };
259
- private advisoryReview: AdvisoryReview | null = null;
260
- private rawPath: string | null = null;
261
- private retainedAuxiliaryWorkspaces: string[] = [];
262
- constructor(
263
- private readonly options: EvaluationOptions,
264
- private readonly context: EvaluationContext,
265
- private readonly trial: number,
266
- private readonly stopAdmission: () => void,
267
- private readonly fixture: Awaited<ReturnType<typeof prepareTrialWorkspace>>,
268
- ) {
269
- this.execution = options.dry ? "not_run" : "completed";
270
- }
271
- async run(): Promise<boolean> {
272
- try {
273
- if (this.fixture.preparationCancelled)
274
- return await this.cancelPreparation();
275
- await this.initializeGradingBases();
276
- await this.runCandidate();
277
- await this.retainCandidateArtifacts();
278
- this.observeCandidate();
279
- await this.prepareCandidateArtifacts();
280
- this.gradeOutput();
281
- await this.gradeShell();
282
- await this.gradeGitHead();
283
- await this.gradeSemantic();
284
- await this.gradeExtension();
285
- this.assess();
286
- await this.runAdvisory();
287
- await this.retainRawCandidate();
288
- return await this.persistTrial();
289
- } finally {
290
- await this.cleanup();
291
- }
292
- }
293
- private async cancelPreparation(): Promise<boolean> {
294
- this.failCandidate(undefined);
295
- await this.retainCandidateArtifacts();
296
- this.observeCandidate();
297
- this.assess();
298
- return this.persistTrial();
299
- }
300
- private async initializeGradingBases(): Promise<void> {
301
- this.gitHeadBase = this.context.preparedGitHead.length
302
- ? await gitHeadRevision(this.fixture.workspace, this.options.signal)
303
- : null;
304
- this.advisoryRevision = this.options.advisoryHost
305
- ? await advisoryBaseRevision(this.fixture.workspace)
306
- : null;
307
- }
308
- private candidateRequest(): Parameters<HostAdapter["run"]>[0] {
309
- return {
310
- candidateRuntimeRoot: this.candidateRuntimeRoot,
311
- prompt: this.fixture.trialPrompt,
312
- ...(this.fixture.trialFollowUpPrompt !== undefined
313
- ? { followUpPrompt: this.fixture.trialFollowUpPrompt }
314
- : {}),
315
- workspace: this.fixture.workspace,
316
- condition: this.options.condition,
317
- fixtureBinDir: this.fixture.fixtureBinDir,
318
- runtimePolicy: this.options.runtimePolicy,
319
- instrumentation: snapshotInstrumentation(
320
- this.context.requestedInstrumentation,
321
- ),
322
- ...candidateMarketplaceRequest(this.context),
323
- ...candidatePluginDirectoriesRequest(this.context),
324
- ...candidateInvocationRequest(this.context),
325
- signal: this.options.signal,
326
- };
327
- }
328
-
329
- private candidateReservedIds(): Set<string> {
330
- return new Set([
331
- "sevro.observation.final-message",
332
- "sevro.observation.workspaces",
333
- ...this.context.artifactRefs.map((item) => item.id),
334
- ...this.additionalObservations.map((item) => item.id),
335
- ...this.options.case.checks.map((item) => item.id),
336
- ...this.context.preparedShell.map(
337
- (item) => `sevro.observation.shell.${item.id}`,
338
- ),
339
- ...(this.context.preparedGitHead.length
340
- ? ["sevro.observation.git-head"]
341
- : []),
342
- ...(this.context.preparedSemantic.length
343
- ? ["sevro.semantic.verdicts"]
344
- : []),
345
- ]);
346
- }
347
-
348
- private acceptCandidateResult(response: HostResult): void {
349
- this.appliedInstrumentation = verifyAppliedInstrumentation(
350
- this.context.requestedInstrumentation,
351
- response.appliedInstrumentation,
352
- this.options.condition,
353
- response.actualCondition,
354
- );
355
- if (
356
- response.finalMessage !== null &&
357
- Buffer.byteLength(response.finalMessage, "utf8") > MAX_FINAL_MESSAGE_BYTES
358
- )
359
- throw new Error("oversized host result");
360
- this.additionalObservations = hostObservations(
361
- response,
362
- this.options.host.id,
363
- new Set([
364
- ...this.context.artifactRefs.map((item) => item.id),
365
- ...this.options.case.checks.map((item) => item.id),
366
- ]),
367
- );
368
- this.producedArtifacts = hostArtifacts(
369
- response,
370
- this.candidateReservedIds(),
371
- );
372
- if (response.executionFailed) {
373
- this.execution = "failed";
374
- this.stopAdmission();
375
- this.diagnostic = {
376
- code: "sevro.host.failed",
377
- message:
378
- codexFailureMessage(response) ?? "host execution did not complete",
379
- };
380
- }
381
- }
382
-
383
- private failCandidate(error: unknown): void {
384
- this.stopAdmission();
385
- this.hostResult =
386
- error instanceof CodexProcessFailure ? error.result : null;
387
- if (this.hostResult) this.acceptCandidateResult(this.hostResult);
388
- this.execution = this.options.signal?.aborted ? "cancelled" : "failed";
389
- this.diagnostic = hostFailureDiagnostic(this.execution, error);
390
- }
391
-
392
- private async runCandidate(): Promise<void> {
393
- if (this.options.dry) return;
394
- const started = performance.now();
395
- try {
396
- ensureNotCancelled(this.options.signal);
397
- this.candidateRuntimeRoot = await allocateCandidateRuntime(
398
- this.options.runtimePolicy,
399
- );
400
- this.hostResult = await this.options.host.run(this.candidateRequest());
401
- this.acceptCandidateResult(this.hostResult);
402
- if (this.options.signal?.aborted) {
403
- this.execution = "cancelled";
404
- this.stopAdmission();
405
- this.diagnostic = hostFailureDiagnostic("cancelled", undefined);
406
- }
407
- } catch (error) {
408
- this.failCandidate(error);
409
- } finally {
410
- this.candidateDurationMs = Math.max(0, performance.now() - started);
411
- }
412
- }
413
-
414
- private async writeTrialFile(
415
- path: string,
416
- bytes: string | Uint8Array,
417
- ): Promise<void> {
418
- try {
419
- await writeFile(path, bytes, { flag: "wx", mode: 0o600 });
420
- } catch (cause) {
421
- throw new Error(
422
- `trial persistence failed; fixture retained at ${this.fixture.workspace}`,
423
- { cause },
424
- );
425
- }
426
- }
427
-
428
- private async retainArtifacts(
429
- artifacts: ReturnType<typeof hostArtifacts>,
430
- prefix = "",
431
- ): Promise<void> {
432
- for (const artifact of artifacts) {
433
- const path = join(
434
- this.context.runDir,
435
- `trial-${this.trial}-${prefix}${artifact.id}.bin`,
436
- );
437
- await this.writeTrialFile(path, artifact.bytes);
438
- this.trialArtifactRefs.push({
439
- id: artifact.id,
440
- path: pathToFileURL(path).href,
441
- sha256: sha256(artifact.bytes),
442
- });
443
- }
444
- }
445
-
446
- private async retainCandidateArtifacts(): Promise<void> {
447
- this.trialArtifactRefs = [...this.context.artifactRefs];
448
- await this.retainArtifacts(this.producedArtifacts, "host-");
449
- await verifyRetainedArtifacts(this.trialArtifactRefs);
450
- const bundle = this.producedArtifacts.find(
451
- (item) => item.id === "sevro.native-transcripts.bundle",
452
- );
453
- if (this.options.runtimePolicy?.nativeTranscripts && bundle)
454
- this.candidateTranscriptRoot = await materializeNativeTranscriptView(
455
- bundle.bytes,
456
- );
457
- }
458
-
459
- private observeCandidate(): void {
460
- const result = this.hostResult;
461
- if (result === null || result.finalMessage == null) {
462
- this.rawDigest = null;
463
- this.completeness = "unavailable";
464
- this.observation = {
465
- id: "sevro.observation.final-message",
466
- source: this.options.host.id,
467
- completeness: this.completeness,
468
- data: {},
469
- };
470
- return;
471
- }
472
- this.rawDigest = sha256(result.finalMessage);
473
- this.completeness = result.complete ? "complete" : "partial";
474
- this.observation = {
475
- id: "sevro.observation.final-message",
476
- source: this.options.host.id,
477
- completeness: this.completeness,
478
- data: {
479
- sha256: this.rawDigest,
480
- byteLength: Buffer.byteLength(result.finalMessage, "utf8"),
481
- },
482
- };
483
- }
484
-
485
- private failGrading(message: string, cancelable = true): void {
486
- if (cancelable && this.options.signal?.aborted) {
487
- this.execution = "cancelled";
488
- this.diagnostic = {
489
- code: "sevro.run.cancelled",
490
- message: "run cancelled",
491
- };
492
- return;
493
- }
494
- this.graderError = true;
495
- this.diagnostic = { code: "sevro.grader.error", message };
496
- this.stopAdmission();
497
- }
498
-
499
- private canGrade(): boolean {
500
- return this.execution === "completed" && !this.graderError;
501
- }
502
-
503
- private async prepareCandidateArtifacts(): Promise<void> {
504
- if (this.execution !== "completed") return;
505
- try {
506
- this.candidateArtifacts = await candidateArtifactRoots(
507
- this.candidateRuntimeRoot,
508
- );
509
- } catch {
510
- this.failGrading("candidate artifact directories are missing or unsafe");
511
- }
512
- }
513
-
514
- private gradeOutput(): void {
515
- if (this.execution !== "completed") return;
516
- try {
517
- const result = this.hostResult;
518
- if (!result) throw new Error("host result is unavailable");
519
- this.checks = gradeOutput(
520
- result.finalMessage,
521
- result.complete,
522
- this.context.prepared,
523
- ).map((check) => ({
524
- ...check,
525
- evidenceRefs: this.rawDigest ? [this.observation.id] : [],
526
- }));
527
- } catch {
528
- this.failGrading("output grading did not complete", false);
529
- }
530
- }
531
-
532
- private async shellProtectedRoots(): Promise<string[]> {
533
- const isolation = this.options.shellIsolation;
534
- if (!isolation)
535
- throw new Error("shell checks require explicit protected source roots");
536
- return evaluationProtectedRoots({
537
- workspace: this.fixture.workspace,
538
- projectRoot: this.context.projectRoot,
539
- resultsRoot: this.options.resultsRoot,
540
- additionalRoots: [
541
- ...isolation.protectedRoots,
542
- ...(this.options.preparationSources
543
- ? [this.options.preparationSources.root]
544
- : []),
545
- ...(this.context.repository ? [this.context.repository.path] : []),
546
- this.context.runStateRoot,
547
- ],
548
- });
549
- }
550
-
551
- private async gradeShellCheck(
552
- check: EvaluationContext["preparedShell"][number],
553
- protectedRoots: string[],
554
- ): Promise<void> {
555
- const result = await runShellCheck(check, {
556
- candidateTranscriptRoot: this.candidateTranscriptRoot,
557
- candidateArtifactRoots: this.candidateArtifacts,
558
- workspace: this.fixture.workspace,
559
- fixtureBinDir: this.fixture.fixtureBinDir,
560
- toolchainBinDir: this.options.shellIsolation?.toolchainBinDir,
561
- runtimePolicy: this.options.runtimePolicy,
562
- uvRuntimeCache: this.options.shellIsolation?.uvRuntimeCache,
563
- protectedRoots,
564
- protectedRootsCanonical: true,
565
- privateStateRoot: join(this.context.stateDir, "shell-sandbox"),
566
- signal: this.options.signal,
567
- });
568
- const graded = assessShellCheck(check, result);
569
- const id = `sevro.observation.shell.${check.id}`;
570
- this.shellObservations.push({
571
- id,
572
- source: "sevro.shell",
573
- completeness: "complete",
574
- data: {
575
- exitCode: result.exitCode,
576
- expectedExitCode: check.expectedExitCode,
577
- ...(result.stdout === null
578
- ? {}
579
- : {
580
- stdoutSha256: sha256(result.stdout),
581
- stdoutByteLength: Buffer.byteLength(result.stdout, "utf8"),
582
- }),
583
- },
584
- });
585
- this.checks.push({
586
- id: check.id,
587
- grader: "sevro.shell",
588
- status: graded.passed ? "passed" : "failed",
589
- detail: graded.detail,
590
- evidenceRefs: [id],
591
- });
592
- }
593
-
594
- private async gradeShell(): Promise<void> {
595
- if (!this.canGrade() || this.context.preparedShell.length === 0) return;
596
- try {
597
- const protectedRoots = await this.shellProtectedRoots();
598
- for (const check of this.context.preparedShell)
599
- await this.gradeShellCheck(check, protectedRoots);
600
- } catch {
601
- this.failGrading("shell grading did not complete");
602
- }
603
- }
604
-
605
- private async gradeGitHead(): Promise<void> {
606
- if (!this.canGrade() || this.context.preparedGitHead.length === 0) return;
607
- try {
608
- const base = this.gitHeadBase;
609
- if (base === null)
610
- throw new Error("Git HEAD base revision is unavailable");
611
- const state = await gitHeadState(
612
- this.fixture.workspace,
613
- base,
614
- this.options.signal,
615
- );
616
- const id = "sevro.observation.git-head";
617
- this.gitHeadObservations.push({
618
- id,
619
- source: "sevro.git-head",
620
- completeness: "complete",
621
- data: {
622
- baseRevision: base,
623
- currentRevision: state.currentRevision,
624
- baseAncestor: state.baseAncestor,
625
- },
626
- });
627
- this.checks.push(
628
- ...this.context.preparedGitHead.map((check) => {
629
- const graded = assessGitHeadCheck(check, base, state);
630
- return {
631
- id: check.id,
632
- grader: "sevro.git-head",
633
- status: graded.passed ? ("passed" as const) : ("failed" as const),
634
- detail: graded.detail,
635
- evidenceRefs: [id],
636
- };
637
- }),
638
- );
639
- } catch {
640
- this.failGrading("Git HEAD grading did not complete");
641
- }
642
- }
643
-
644
- private completeCandidateMessage(): string | null {
645
- const result = this.hostResult;
646
- if (result?.finalMessage == null || !result.complete) return null;
647
- return result.finalMessage;
648
- }
649
-
650
- private async gradeSemantic(): Promise<void> {
651
- const graded = await gradeSemanticChecks(
652
- {
653
- checks: this.context.preparedSemantic,
654
- declaredChecks: this.options.case.checks,
655
- host: this.options.semanticHost,
656
- message: this.completeCandidateMessage(),
657
- candidateWorkspace: this.fixture.workspace,
658
- candidateTranscriptRoot: this.candidateTranscriptRoot,
659
- candidateArtifactRoots: this.candidateArtifacts,
660
- projectRoot: this.context.projectRoot,
661
- reservedWorkspace:
662
- this.context.reservedSemanticWorkspaces[this.trial - 1],
663
- runDir: this.context.runDir,
664
- trial: this.trial,
665
- runtimePolicy: this.options.runtimePolicy,
666
- signal: this.options.signal,
667
- },
668
- {
669
- artifactRefs: this.trialArtifactRefs,
670
- canGrade: () => this.canGrade(),
671
- failGrading: (message, cancelable) => {
672
- this.failGrading(message, cancelable);
673
- },
674
- retainArtifacts: (artifacts) => this.retainArtifacts(artifacts),
675
- writeTrialFile: (path, bytes) => this.writeTrialFile(path, bytes),
676
- retainWorkspace: (workspace) =>
677
- this.retainAuxiliaryWorkspace(workspace),
678
- },
679
- );
680
- this.semanticObservations.push(...graded.observations);
681
- this.checks.push(...graded.checks);
682
- }
683
-
684
- private trialObservations(): TrialObservation[] {
685
- return [
686
- this.observation,
687
- ...this.shellObservations,
688
- ...this.gitHeadObservations,
689
- ...this.semanticObservations,
690
- ...this.additionalObservations,
691
- {
692
- id: "sevro.observation.workspaces",
693
- source: "sevro.runner",
694
- completeness: "complete",
695
- data: {
696
- candidate: this.fixture.workspace,
697
- retainedAuxiliary: this.retainedAuxiliaryWorkspaces,
698
- },
699
- },
700
- ];
701
- }
702
-
703
- private extensionEvaluationRequest(): Parameters<
704
- NonNullable<EvaluationOptions["extension"]>["session"]["evaluate"]
705
- >[0] {
706
- return {
707
- caseId: this.options.case.id,
708
- execution: { status: this.execution },
709
- observations: this.trialObservations().map((observation) =>
710
- observation === this.observation
711
- ? {
712
- ...observation,
713
- completeness: this.completeness,
714
- data: {
715
- ...observation.data,
716
- ...extensionFinalMessageData(this.hostResult),
717
- },
718
- }
719
- : observation,
720
- ),
721
- builtinChecks: this.checks.map((check) => ({
722
- id: check.id,
723
- status: check.status,
724
- ...(check.detail ? { detail: check.detail } : {}),
725
- evidenceRefs: check.evidenceRefs ?? [],
726
- })),
727
- artifacts: this.trialArtifactRefs,
728
- extensionData: this.context.extensionData,
729
- };
730
- }
731
-
732
- private acceptExtensionResult(result: EvaluationResult): void {
733
- const declared = new Map(
734
- this.context.extensionDeclarations.map((check) => [
735
- check.id,
736
- check.grader,
737
- ]),
738
- );
739
- this.checks.push(
740
- ...result.checks.map((check) => extensionCheckResult(check, declared)),
741
- );
742
- this.extensionMetrics = result.metrics;
743
- this.domainOutcomes = result.domainOutcomes ?? [];
744
- this.taskPolicyRecommendation = result.taskVerdictRecommendation ?? null;
745
- }
746
-
747
- private async gradeExtension(): Promise<void> {
748
- const extension = this.options.extension;
749
- if (!extension || !this.canGrade()) return;
750
- try {
751
- this.acceptExtensionResult(
752
- await extension.session.evaluate(this.extensionEvaluationRequest()),
753
- );
754
- } catch (error) {
755
- this.failGrading(
756
- error instanceof ExtensionSourceChangedError
757
- ? "extension source inputs changed during evaluation; assessment was refused. Finish changing evaluator inputs, then start a fresh evaluation with inputs held stable."
758
- : "extension grading did not complete",
759
- );
760
- }
761
- }
762
-
763
- private assess(): void {
764
- const declaredOrder = new Map(
765
- this.options.case.checks.map((check, index) => [check.id, index]),
766
- );
767
- this.checks.sort((left, right) =>
768
- compareDeclaredChecks(left, right, declaredOrder),
769
- );
770
- const defaultAssessment = assessTrial({
771
- execution: this.execution,
772
- declaredChecks: this.options.case.checks
773
- .filter((check) => !this.context.replaced.has(check.grader))
774
- .map((check) => check.id),
775
- checks: this.checks,
776
- graderError: this.graderError,
777
- requiredEvidenceUnavailable: this.options.case.requiredEvidence.some(
778
- (id) =>
779
- !this.producedArtifacts.some((item) => item.id === id) &&
780
- !this.trialObservations().some(
781
- (item) => item.id === id && item.completeness === "complete",
782
- ),
783
- ),
784
- });
785
- this.assessment = applyTaskVerdictPolicy(
786
- defaultAssessment,
787
- this.taskPolicyRecommendation,
788
- Boolean(
789
- this.options.extension?.session.identity.selectedTaskVerdictPolicy,
790
- ),
791
- );
792
- }
793
- private async retainAdvisoryResponse(
794
- host: HostAdapter,
795
- response: HostResult,
796
- review: AdvisoryReview,
797
- ): Promise<void> {
798
- if (
799
- response.finalMessage !== null &&
800
- Buffer.byteLength(response.finalMessage, "utf8") <= 64 * 1024
801
- ) {
802
- const path = join(
803
- this.context.runDir,
804
- `trial-${this.trial}-advisory.json`,
805
- );
806
- await this.writeTrialFile(path, response.finalMessage);
807
- review.rawResult = {
808
- source: host.id,
809
- path: pathToFileURL(path).href,
810
- sha256: sha256(response.finalMessage),
811
- };
812
- this.trialArtifactRefs.push({
813
- id: "sevro.advisory.response",
814
- path: pathToFileURL(path).href,
815
- sha256: sha256(response.finalMessage),
816
- });
817
- }
818
- }
819
-
820
- private async cleanupAdvisoryWorkspace(
821
- workspace: string | null,
822
- successful: boolean,
823
- ): Promise<void> {
824
- if (!workspace) return;
825
- if (!successful) return this.retainAuxiliaryWorkspace(workspace);
826
- try {
827
- await removeWorkspace(workspace);
828
- } catch {
829
- console.warn(`advisory fixture cleanup failed; retained at ${workspace}`);
830
- }
831
- }
832
-
833
- private async executeAdvisoryReview(
834
- host: HostAdapter,
835
- review: AdvisoryReview,
836
- ): Promise<void> {
837
- let workspace: string | null = null;
838
- try {
839
- const baseRevision = this.advisoryRevision;
840
- if (baseRevision === null)
841
- throw new Error("advisory base revision is unavailable");
842
- workspace = await buildBlindAdvisoryFixture(this.fixture.workspace, {
843
- baseRevision,
844
- excludedPaths: this.options.advisoryExcludedPaths,
845
- });
846
- const response = await host.run({
847
- candidateTranscriptRoot: this.candidateTranscriptRoot,
848
- candidateArtifactRoots: this.candidateArtifacts,
849
- prompt: advisoryPrompt(this.fixture.trialPrompt, this.checks),
850
- runtimePolicy: gradingRuntimePolicy(this.options.runtimePolicy),
851
- runtimeRole: "advisory",
852
- workspace,
853
- condition: "passive",
854
- signal: this.options.signal,
855
- });
856
- review.usage = usage(response);
857
- const artifacts = scopedHostArtifacts(
858
- response,
859
- "sevro.advisory.",
860
- this.trialArtifactRefs,
861
- this.options.case.checks,
862
- "sevro.advisory.response",
863
- );
864
- await this.retainArtifacts(artifacts);
865
- await this.retainAdvisoryResponse(host, response, review);
866
- review.assessment = completeAdvisoryAssessment(response);
867
- review.status = "completed";
868
- } catch (error) {
869
- if (isTrialPersistenceFailure(error)) throw error;
870
- review.status = "failed";
871
- } finally {
872
- await this.cleanupAdvisoryWorkspace(
873
- workspace,
874
- review.status === "completed",
875
- );
876
- }
877
- }
878
- private retainAuxiliaryWorkspace(workspace: string): Promise<void> {
879
- this.retainedAuxiliaryWorkspaces.push(workspace);
880
- this.context.retainedWorkspaces.add(workspace);
881
- return retainWorkspace(workspace);
882
- }
883
-
884
- private async runAdvisory(): Promise<void> {
885
- const host = this.options.advisoryHost;
886
- if (!host) return;
887
- const review = defaultAdvisoryReview(host);
888
- this.advisoryReview = review;
889
- if (this.execution !== "completed" || this.options.signal?.aborted) return;
890
- await this.executeAdvisoryReview(host, review);
891
- }
892
-
893
- private async retainRawCandidate(): Promise<void> {
894
- await verifyRetainedArtifacts(this.trialArtifactRefs);
895
- const message = this.hostResult?.finalMessage;
896
- if (message == null) {
897
- this.rawPath = null;
898
- return;
899
- }
900
- this.rawPath = join(this.context.runDir, `trial-${this.trial}-raw.txt`);
901
- await this.writeTrialFile(this.rawPath, message);
902
- }
903
-
904
- private trialEvidenceRecord(): RunEvidenceData["trials"][number] {
905
- return {
906
- caseId: this.options.case.id,
907
- trial: this.trial,
908
- executionMode: this.options.dry ? "dry" : "executed",
909
- candidateDurationMs: this.candidateDurationMs,
910
- condition: {
911
- requested: this.options.condition,
912
- actual: this.hostResult?.actualCondition ?? "unknown",
913
- appliedInstrumentation: this.appliedInstrumentation,
914
- },
915
- observationCompleteness: this.completeness,
916
- observations: this.trialObservations(),
917
- metrics: this.extensionMetrics,
918
- domainOutcomes: this.domainOutcomes,
919
- taskVerdictPolicy: selectedTrialTaskPolicy(
920
- this.options,
921
- this.taskPolicyRecommendation,
922
- ),
923
- advisoryReview: this.advisoryReview,
924
- routes: this.context.routes,
925
- usage: usage(this.hostResult),
926
- rawResult: {
927
- source: this.options.host.id,
928
- path: this.rawPath ? pathToFileURL(this.rawPath).href : null,
929
- sha256: this.rawDigest,
930
- },
931
- artifactRefs: this.trialArtifactRefs,
932
- };
933
- }
934
-
935
- private async persistTrial(): Promise<boolean> {
936
- const evidence = this.trialEvidenceRecord();
937
- const artifactPath = join(this.context.runDir, `trial-${this.trial}.json`);
938
- const trialSummary: TrialSummary = {
939
- trial: this.trial,
940
- ...this.assessment,
941
- checks: this.checks,
942
- domainOutcomes: this.domainOutcomes,
943
- artifactPath,
944
- };
945
- try {
946
- await atomicWriteJson(artifactPath, {
947
- format: "sevro.trial-evidence.v1",
948
- caseId: this.options.case.id,
949
- trial: this.trial,
950
- result: trialSummary,
951
- evidence,
952
- });
953
- } catch {
954
- throw new Error(
955
- `trial persistence failed; fixture retained at ${this.fixture.workspace}`,
956
- );
957
- }
958
- this.persisted = true;
959
- this.context.trialSummaries.push(trialSummary);
960
- this.context.trialSummaries.sort((left, right) => left.trial - right.trial);
961
- this.context.trialEvidence.push(evidence);
962
- this.context.trialEvidence.sort((left, right) => left.trial - right.trial);
963
- if (this.diagnostic)
964
- this.context.diagnostics.push({
965
- trial: this.trial,
966
- diagnostic: this.diagnostic,
967
- });
968
- checkpointEvaluation(this.context, "active");
969
- return !(
970
- (this.execution !== "completed" && this.execution !== "not_run") ||
971
- this.graderError
972
- );
973
- }
974
- private async cleanup(): Promise<void> {
975
- await releaseCandidateRuntime(this.candidateRuntimeRoot);
976
- if (this.candidateTranscriptRoot)
977
- await rm(this.candidateTranscriptRoot, { recursive: true, force: true });
978
- if (this.persisted && !this.retainFailedWorkspace()) {
979
- try {
980
- await clearFixtureContents(this.fixture.workspace);
981
- this.context.retainedWorkspaces.delete(this.fixture.reservation);
982
- } catch {
983
- console.warn(
984
- `fixture cleanup failed; retained at ${this.fixture.workspace}`,
985
- );
986
- }
987
- }
988
- }
989
- private retainFailedWorkspace(): boolean {
990
- return (
991
- this.execution === "failed" ||
992
- this.execution === "cancelled" ||
993
- this.graderError ||
994
- this.assessment.grading.status === "unavailable" ||
995
- this.assessment.task.verdict === "failed"
996
- );
997
- }
998
- }
999
- export async function executeEvaluationTrials(
1000
- options: EvaluationOptions,
1001
- context: EvaluationContext,
1002
- ): Promise<void> {
1003
- await scheduleTrials(
1004
- { count: options.trialCount, jobs: context.jobs, signal: options.signal },
1005
- async (trial, stopAdmission) => {
1006
- const fixture = await prepareTrialWorkspace(options, context, trial);
1007
- return new EvaluationTrial(
1008
- options,
1009
- context,
1010
- trial,
1011
- stopAdmission,
1012
- fixture,
1013
- ).run();
1014
- },
1015
- );
1016
- }