@bjoernrochel/sevro 0.1.0-rc.7 → 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 (230) 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 +17 -3
  7. package/docs/codex-host.md +155 -49
  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 +141 -4
  25. package/docs/run-settings-v1.md +147 -0
  26. package/docs/runtime-v1.md +307 -18
  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 +63 -7
  64. package/src/advisory.ts +2 -2
  65. package/src/candidate-artifacts.ts +206 -0
  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 +13 -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} +133 -115
  103. package/src/evaluation/trial.ts +800 -0
  104. package/src/evaluation/types.ts +207 -0
  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} +15 -76
  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/fixtures/workspace-state.ts +212 -0
  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 +57 -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 +91 -14
  148. package/src/hosts/claude-skill-activations.ts +169 -0
  149. package/src/hosts/claude-state.ts +46 -16
  150. package/src/hosts/claude-tool-calls.ts +13 -10
  151. package/src/hosts/claude.ts +122 -73
  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 -54
  158. package/src/hosts/codex-profile.ts +50 -21
  159. package/src/hosts/codex-session-files.ts +1 -9
  160. package/src/hosts/codex-state.ts +141 -19
  161. package/src/hosts/codex.ts +221 -179
  162. package/src/hosts/isolation-common.ts +15 -11
  163. package/src/hosts/isolation-roots.ts +13 -20
  164. package/src/hosts/linux-sandbox.ts +27 -3
  165. package/src/hosts/mac-sandbox.ts +111 -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 +221 -45
  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 +34 -15
  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/run-owner.ts +4 -4
  186. package/src/{runtime-config.ts → runtime/config.ts} +182 -44
  187. package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
  188. package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
  189. package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
  190. package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
  191. package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
  192. package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
  193. package/src/{runtime-query.ts → runtime/query.ts} +5 -5
  194. package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
  195. package/src/runtime/shared-cache.ts +130 -0
  196. package/src/{runtime-state.ts → runtime/state.ts} +28 -11
  197. package/src/schema-types.ts +73 -161
  198. package/src/shell.ts +4 -0
  199. package/src/transcript-retention.ts +146 -0
  200. package/src/value-guards.ts +22 -0
  201. package/docs/assets/sevro-logo.png +0 -0
  202. package/docs/assets/sevro-symbol.svg +0 -1
  203. package/docs/assets/sevro-wordmark.svg +0 -1
  204. package/docs/cache-ownership-validation.md +0 -49
  205. package/docs/claude-continuation-validation.md +0 -47
  206. package/docs/concurrency-validation.md +0 -86
  207. package/docs/configuration-root-validation.md +0 -70
  208. package/docs/engine-slice.md +0 -114
  209. package/docs/evidence/runtime-startup-30/README.md +0 -185
  210. package/docs/evidence/runtime-startup-30/after.json +0 -319
  211. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
  212. package/docs/evidence/runtime-startup-30/before.json +0 -403
  213. package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
  214. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
  215. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
  216. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
  217. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
  218. package/docs/evidence.md +0 -17
  219. package/docs/fixture-cleanup-validation.md +0 -70
  220. package/docs/guide-verification.json +0 -239
  221. package/src/evaluation-context.ts +0 -257
  222. package/src/evaluation-fixture.ts +0 -313
  223. package/src/evaluation-run.ts +0 -198
  224. package/src/evaluation-trial.ts +0 -946
  225. package/src/evaluation-types.ts +0 -149
  226. package/src/hosts/claude-nested-skills.ts +0 -272
  227. package/src/hosts/codex-command-output.ts +0 -510
  228. package/src/hosts/codex-executor-command.ts +0 -291
  229. package/src/hosts/codex-skill-reads.ts +0 -609
  230. /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
package/src/engine.ts CHANGED
@@ -1,11 +1,19 @@
1
- export { EvaluationConfigurationError } from "./evaluation-types";
1
+ export {
2
+ EvaluationConfigurationError,
3
+ HostExecutionFailure,
4
+ } from "./evaluation/types";
2
5
  export type {
3
6
  HostResult,
4
7
  HostAdapter,
5
8
  ResolvedCase,
6
9
  EvaluationOptions,
10
+ EvaluationProgress,
7
11
  CliResult,
8
- } from "./evaluation-types";
9
- export { runEvaluation } from "./evaluation-run";
10
- export { loadRuntimeConfiguration } from "./runtime-config";
11
- export type { RuntimeConfiguration, RuntimePolicy } from "./runtime-config";
12
+ } from "./evaluation/types";
13
+ export { runEvaluation } from "./evaluation/run";
14
+ export { loadRuntimeConfiguration } from "./runtime/config";
15
+ export type { RuntimeConfiguration, RuntimePolicy } from "./runtime/config";
16
+ export type {
17
+ RuntimeArtifactDirectory,
18
+ CandidateArtifactRoots,
19
+ } from "./candidate-artifacts";
@@ -0,0 +1,150 @@
1
+ import {
2
+ advisoryPrompt,
3
+ parseAdvisoryAssessment,
4
+ type AdvisoryAssessment,
5
+ } from "../advisory";
6
+ import type { CandidateArtifactRoots } from "../candidate-artifacts";
7
+ import type { TrialEvidenceWriter } from "../evidence/trial-writer";
8
+ import { usage } from "../evidence/usage";
9
+ import { buildBlindAdvisoryFixture } from "../fixtures/advisory";
10
+ import { removeWorkspace } from "../fixtures/workspace-state";
11
+ import { HostDiagnosticPersistenceError } from "../host-diagnostics";
12
+ import type { CheckOutcome } from "../results";
13
+ import type { AdvisoryReviewData } from "../schema-types";
14
+ import type { RuntimePolicy } from "../runtime/config";
15
+ import { gradingRuntimePolicy, scopedHostArtifacts } from "./grading-host";
16
+ import {
17
+ TrialPersistenceError,
18
+ type HostAdapter,
19
+ type HostResult,
20
+ } from "./types";
21
+
22
+ export type AdvisoryReview = AdvisoryReviewData;
23
+
24
+ interface AdvisoryInput {
25
+ readonly host: HostAdapter;
26
+ readonly candidateCompleted: boolean;
27
+ readonly candidateWorkspace: string;
28
+ readonly candidateTranscriptRoot?: string;
29
+ readonly candidateArtifactRoots?: CandidateArtifactRoots;
30
+ readonly baseRevision: string | null;
31
+ readonly excludedPaths?: string[];
32
+ readonly task: string;
33
+ readonly checks: readonly CheckOutcome[];
34
+ readonly declaredChecks: { id: string }[];
35
+ readonly runtimePolicy?: RuntimePolicy;
36
+ readonly signal?: AbortSignal;
37
+ }
38
+
39
+ interface AdvisoryEffects {
40
+ readonly evidence: Pick<
41
+ TrialEvidenceWriter,
42
+ "artifactRefs" | "retainArtifacts" | "retainAdvisoryResponse"
43
+ >;
44
+ retainWorkspace(workspace: string): Promise<void>;
45
+ }
46
+
47
+ function defaultAdvisoryReview(host: HostAdapter): AdvisoryReview {
48
+ return {
49
+ status: "not_run",
50
+ assessment: null,
51
+ usage: usage(null),
52
+ rawResult: { source: host.id, path: null, sha256: null },
53
+ };
54
+ }
55
+
56
+ function completeAdvisoryAssessment(response: HostResult): AdvisoryAssessment {
57
+ if (
58
+ response.executionFailed ||
59
+ !response.complete ||
60
+ response.finalMessage === null
61
+ )
62
+ throw new Error("advisory response is incomplete");
63
+ return parseAdvisoryAssessment(response.finalMessage);
64
+ }
65
+
66
+ async function cleanupAdvisoryWorkspace(
67
+ workspace: string | null,
68
+ successful: boolean,
69
+ effects: AdvisoryEffects,
70
+ ): Promise<void> {
71
+ if (!workspace) return;
72
+ if (!successful) return effects.retainWorkspace(workspace);
73
+ try {
74
+ await removeWorkspace(workspace);
75
+ } catch {
76
+ console.warn(`advisory fixture cleanup failed; retained at ${workspace}`);
77
+ }
78
+ }
79
+
80
+ async function executeAdvisoryReview(
81
+ input: AdvisoryInput,
82
+ effects: AdvisoryEffects,
83
+ review: AdvisoryReview,
84
+ ): Promise<void> {
85
+ let workspace: string | null = null;
86
+ try {
87
+ if (input.baseRevision === null)
88
+ throw new Error("advisory base revision is unavailable");
89
+ workspace = await buildBlindAdvisoryFixture(input.candidateWorkspace, {
90
+ baseRevision: input.baseRevision,
91
+ excludedPaths: input.excludedPaths,
92
+ });
93
+ const response = await input.host.run({
94
+ retainHostDiagnostics: (bytes) =>
95
+ effects.evidence.retainArtifacts([
96
+ {
97
+ id: "sevro.advisory.sevro.host.diagnostics",
98
+ bytes: Buffer.from(bytes),
99
+ },
100
+ ]),
101
+ candidateTranscriptRoot: input.candidateTranscriptRoot,
102
+ candidateArtifactRoots: input.candidateArtifactRoots,
103
+ prompt: advisoryPrompt(input.task, [...input.checks]),
104
+ runtimePolicy: gradingRuntimePolicy(input.runtimePolicy),
105
+ runtimeRole: "advisory",
106
+ workspace,
107
+ condition: "passive",
108
+ signal: input.signal,
109
+ });
110
+ review.usage = usage(response);
111
+ const artifacts = scopedHostArtifacts(
112
+ response,
113
+ "sevro.advisory.",
114
+ effects.evidence.artifactRefs,
115
+ input.declaredChecks,
116
+ "sevro.advisory.response",
117
+ );
118
+ await effects.evidence.retainArtifacts(artifacts);
119
+ review.rawResult = await effects.evidence.retainAdvisoryResponse(
120
+ input.host.id,
121
+ response.finalMessage,
122
+ );
123
+ review.assessment = completeAdvisoryAssessment(response);
124
+ review.status = "completed";
125
+ } catch (error) {
126
+ if (
127
+ error instanceof HostDiagnosticPersistenceError ||
128
+ error instanceof TrialPersistenceError
129
+ )
130
+ throw error;
131
+ review.status = "failed";
132
+ } finally {
133
+ await cleanupAdvisoryWorkspace(
134
+ workspace,
135
+ review.status === "completed",
136
+ effects,
137
+ );
138
+ }
139
+ }
140
+
141
+ /** Advisory execution depends on candidate completion, independently of task grading. */
142
+ export async function runAdvisoryReview(
143
+ input: AdvisoryInput,
144
+ effects: AdvisoryEffects,
145
+ ): Promise<AdvisoryReview> {
146
+ const review = defaultAdvisoryReview(input.host);
147
+ if (!input.candidateCompleted || input.signal?.aborted) return review;
148
+ await executeAdvisoryReview(input, effects, review);
149
+ return review;
150
+ }
@@ -1,40 +1,83 @@
1
1
  import { isAbsolute } from "node:path";
2
- import { validInlineFixture } from "./case-validation";
2
+ import { validQualifiedId } from "../value-guards";
3
+ import { validInlineFixture } from "../case-validation";
3
4
  import {
4
5
  EvaluationConfigurationError,
5
6
  type EvaluationOptions,
6
7
  type HostAdapter,
7
8
  type ResolvedCase,
8
- } from "./evaluation-types";
9
+ } from "./types";
9
10
  import {
10
11
  prepareGeneratedFixture,
11
12
  type GeneratedFixture,
12
- } from "./generated-fixture";
13
+ } from "../fixtures/generated";
13
14
  import {
14
15
  prepareGitHeadChecks,
15
16
  type GitHeadCheckDeclaration,
16
- } from "./graders/git-head";
17
+ type PreparedGitHeadCheck,
18
+ } from "../graders/git-head";
17
19
  import {
18
20
  isOutputGrader,
19
21
  prepareOutputChecks,
20
22
  type OutputCheckDeclaration,
21
- } from "./graders/output";
23
+ type PreparedOutputCheck,
24
+ } from "../graders/output";
22
25
  import {
23
26
  prepareSemanticChecks,
24
27
  type SemanticCheckDeclaration,
25
- } from "./graders/semantic";
28
+ type PreparedSemanticCheck,
29
+ } from "../graders/semantic";
26
30
  import {
27
31
  prepareShellChecks,
28
32
  type ShellCheckDeclaration,
29
- } from "./graders/shell";
30
- import { hashJson } from "./identity";
31
- import { fixtureParts } from "./preparation";
33
+ type PreparedShellCheck,
34
+ } from "../graders/shell";
35
+ import { hashJson } from "../identity";
36
+ import { fixtureParts } from "../fixtures/preparation";
32
37
  import {
33
38
  prepareRepositoryFixture,
34
39
  type RepositoryFixture,
35
- } from "./repository-fixture";
36
- import { resolvedFixture } from "./resolved-case";
37
- import { isOptionalNonblankString, isRecord } from "./value-guards";
40
+ } from "../fixtures/repository";
41
+ import { resolvedFixture } from "../resolved-case";
42
+ import { isOptionalNonblankString, isRecord } from "../value-guards";
43
+ import type { FixtureSource } from "../fixtures/create";
44
+
45
+ interface CaseCheckDeclarations {
46
+ allBuiltinDeclarations: OutputCheckDeclaration[];
47
+ allShellDeclarations: ShellCheckDeclaration[];
48
+ allGitHeadDeclarations: GitHeadCheckDeclaration[];
49
+ allSemanticDeclarations: SemanticCheckDeclaration[];
50
+ extensionDeclarations: ResolvedCase["checks"];
51
+ replacedBuiltinGraders: string[];
52
+ replaced: Set<string>;
53
+ }
54
+
55
+ interface ActiveCheckDeclarations {
56
+ readonly builtinDeclarations: OutputCheckDeclaration[];
57
+ readonly shellDeclarations: ShellCheckDeclaration[];
58
+ readonly gitHeadDeclarations: GitHeadCheckDeclaration[];
59
+ readonly semanticDeclarations: SemanticCheckDeclaration[];
60
+ }
61
+
62
+ interface BuiltInCheckPlan {
63
+ readonly prepared: PreparedOutputCheck[];
64
+ readonly preparedShell: PreparedShellCheck[];
65
+ readonly preparedGitHead: PreparedGitHeadCheck[];
66
+ readonly preparedSemantic: PreparedSemanticCheck[];
67
+ }
68
+
69
+ export interface PreparedChecks
70
+ extends ActiveCheckDeclarations, BuiltInCheckPlan {
71
+ readonly extensionDeclarations: ResolvedCase["checks"];
72
+ readonly replacedBuiltinGraders: string[];
73
+ readonly replaced: ReadonlySet<string>;
74
+ }
75
+
76
+ export interface EvaluationCheckPlan {
77
+ readonly source: FixtureSource<null>;
78
+ readonly jobs: number;
79
+ readonly checks: PreparedChecks;
80
+ }
38
81
 
39
82
  function validateRootOptions(options: EvaluationOptions): void {
40
83
  if (
@@ -139,23 +182,23 @@ function validateInlineEvaluationFixture(
139
182
  );
140
183
  }
141
184
 
142
- function prepareEvaluationFixture(options: EvaluationOptions): {
143
- generated: GeneratedFixture | null;
144
- repositoryFixture: RepositoryFixture | null;
145
- } {
185
+ function prepareEvaluationFixture(
186
+ options: EvaluationOptions,
187
+ ): FixtureSource<null> {
146
188
  const fixture = evaluationFixtureObject(options.case.fixture);
147
189
  if ("kind" in fixture)
148
190
  return {
149
- generated: generatedEvaluationFixture(fixture),
150
- repositoryFixture: null,
191
+ kind: "generated",
192
+ fixture: generatedEvaluationFixture(fixture),
151
193
  };
152
194
  if (Object.hasOwn(fixture, "sourceRef"))
153
195
  return {
154
- generated: null,
155
- repositoryFixture: repositoryEvaluationFixture(fixture),
196
+ kind: "repository",
197
+ fixture: repositoryEvaluationFixture(fixture),
198
+ repository: null,
156
199
  };
157
200
  validateInlineEvaluationFixture(fixture);
158
- return { generated: null, repositoryFixture: null };
201
+ return { kind: "inline", files: options.case.fixture.files };
159
202
  }
160
203
 
161
204
  function validateTrialInteger(value: number, message: string): void {
@@ -173,15 +216,12 @@ function validatePassThreshold(value: number): void {
173
216
  function validateRequiredEvidence(ids: string[]): void {
174
217
  if (
175
218
  new Set(ids).size !== ids.length ||
176
- ids.some((id) => !/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/.test(id))
219
+ ids.some((id) => !validQualifiedId(id))
177
220
  )
178
221
  throw new EvaluationConfigurationError("invalid required evidence IDs");
179
222
  }
180
223
 
181
- function prepareTrialParameters(
182
- options: EvaluationOptions,
183
- state: ReturnType<typeof prepareEvaluationFixture>,
184
- ) {
224
+ function prepareTrialParameters(options: EvaluationOptions): number {
185
225
  validateTrialInteger(
186
226
  options.trialCount,
187
227
  "trial count must be a positive integer",
@@ -190,10 +230,10 @@ function prepareTrialParameters(
190
230
  validateTrialInteger(jobs, "jobs must be a positive integer");
191
231
  validatePassThreshold(options.passThreshold);
192
232
  validateRequiredEvidence(options.case.requiredEvidence);
193
- return { ...state, jobs };
233
+ return jobs;
194
234
  }
195
235
 
196
- function declaredCaseChecks(options: EvaluationOptions) {
236
+ function declaredCaseChecks(options: EvaluationOptions): CaseCheckDeclarations {
197
237
  const allBuiltinDeclarations = options.case.checks.filter((check) =>
198
238
  isOutputGrader(check.grader),
199
239
  ) as OutputCheckDeclaration[];
@@ -227,8 +267,6 @@ function declaredCaseChecks(options: EvaluationOptions) {
227
267
  };
228
268
  }
229
269
 
230
- type CaseCheckDeclarations = ReturnType<typeof declaredCaseChecks>;
231
-
232
270
  function validateBuiltInReplacements(context: CaseCheckDeclarations): void {
233
271
  const {
234
272
  allBuiltinDeclarations,
@@ -260,7 +298,9 @@ function validateBuiltInReplacements(context: CaseCheckDeclarations): void {
260
298
  );
261
299
  }
262
300
 
263
- function activeCheckDeclarations(context: CaseCheckDeclarations) {
301
+ function activeCheckDeclarations(
302
+ context: CaseCheckDeclarations,
303
+ ): ActiveCheckDeclarations {
264
304
  const {
265
305
  allBuiltinDeclarations,
266
306
  allShellDeclarations,
@@ -288,10 +328,6 @@ function activeCheckDeclarations(context: CaseCheckDeclarations) {
288
328
  };
289
329
  }
290
330
 
291
- type CaseCheckPlan = ReturnType<typeof prepareTrialParameters> &
292
- CaseCheckDeclarations &
293
- ReturnType<typeof activeCheckDeclarations>;
294
-
295
331
  function validateSemanticRoute(
296
332
  options: EvaluationOptions,
297
333
  checks: SemanticCheckDeclaration[],
@@ -306,11 +342,8 @@ function validateSemanticRoute(
306
342
  );
307
343
  }
308
344
 
309
- function validateAdvisoryGitFixture(
310
- options: EvaluationOptions,
311
- generated: GeneratedFixture | null,
312
- ): void {
313
- if (!generated && !options.case.fixture.sourceRef)
345
+ function validateAdvisoryGitFixture(source: FixtureSource<null>): void {
346
+ if (source.kind === "inline")
314
347
  throw new EvaluationConfigurationError(
315
348
  "advisory review requires a Git fixture",
316
349
  );
@@ -335,7 +368,7 @@ function validateAdvisoryExclusions(paths: string[]): void {
335
368
 
336
369
  function validateAdvisoryRoute(
337
370
  options: EvaluationOptions,
338
- generated: GeneratedFixture | null,
371
+ source: FixtureSource<null>,
339
372
  ): void {
340
373
  const host = options.advisoryHost;
341
374
  if (!host) {
@@ -345,7 +378,7 @@ function validateAdvisoryRoute(
345
378
  );
346
379
  return;
347
380
  }
348
- validateAdvisoryGitFixture(options, generated);
381
+ validateAdvisoryGitFixture(source);
349
382
  validateAdvisoryIdentity(host);
350
383
  validateAdvisoryExclusions(options.advisoryExcludedPaths ?? []);
351
384
  }
@@ -367,12 +400,11 @@ function validateShellRoute(
367
400
  );
368
401
  }
369
402
 
370
- function validateGitHeadRoute(context: CaseCheckPlan): void {
371
- if (
372
- context.gitHeadDeclarations.length &&
373
- !context.generated &&
374
- !context.repositoryFixture
375
- )
403
+ function validateGitHeadRoute(
404
+ source: FixtureSource<null>,
405
+ checks: GitHeadCheckDeclaration[],
406
+ ): void {
407
+ if (checks.length && source.kind === "inline")
376
408
  throw new EvaluationConfigurationError(
377
409
  "Git HEAD checks require a Git fixture",
378
410
  );
@@ -443,8 +475,8 @@ function validateInlinePaths(files: ResolvedCase["fixture"]["files"]): void {
443
475
 
444
476
  function prepareBuiltInChecks(
445
477
  options: EvaluationOptions,
446
- context: CaseCheckPlan,
447
- ) {
478
+ context: CaseCheckDeclarations,
479
+ ): BuiltInCheckPlan {
448
480
  const {
449
481
  allBuiltinDeclarations,
450
482
  allShellDeclarations,
@@ -483,28 +515,32 @@ function prepareBuiltInChecks(
483
515
 
484
516
  function prepareCheckPlan(
485
517
  options: EvaluationOptions,
486
- state: ReturnType<typeof prepareTrialParameters>,
487
- ) {
518
+ source: FixtureSource<null>,
519
+ ): PreparedChecks {
488
520
  const declarations = declaredCaseChecks(options);
489
521
  validateBuiltInReplacements(declarations);
490
- const context = {
491
- ...state,
492
- ...declarations,
493
- ...activeCheckDeclarations(declarations),
494
- };
495
- validateSemanticRoute(options, context.semanticDeclarations);
496
- validateAdvisoryRoute(options, context.generated);
497
- validateShellRoute(options, context.shellDeclarations);
498
- validateGitHeadRoute(context);
522
+ const active = activeCheckDeclarations(declarations);
523
+ validateSemanticRoute(options, active.semanticDeclarations);
524
+ validateAdvisoryRoute(options, source);
525
+ validateShellRoute(options, active.shellDeclarations);
526
+ validateGitHeadRoute(source, active.gitHeadDeclarations);
499
527
  validateCaseCheckIds(options.case.checks);
500
- validateExtensionGraders(options, context.extensionDeclarations);
528
+ validateExtensionGraders(options, declarations.extensionDeclarations);
501
529
  validateResolvedExtensionCase(options);
502
- return { ...context, ...prepareBuiltInChecks(options, context) };
530
+ return {
531
+ ...active,
532
+ ...prepareBuiltInChecks(options, declarations),
533
+ extensionDeclarations: declarations.extensionDeclarations,
534
+ replacedBuiltinGraders: declarations.replacedBuiltinGraders,
535
+ replaced: declarations.replaced,
536
+ };
503
537
  }
504
538
 
505
- export function prepareEvaluationChecks(options: EvaluationOptions) {
539
+ export function prepareEvaluationChecks(
540
+ options: EvaluationOptions,
541
+ ): EvaluationCheckPlan {
506
542
  validateEvaluationInputs(options);
507
- const fixture = prepareEvaluationFixture(options);
508
- const trials = prepareTrialParameters(options, fixture);
509
- return prepareCheckPlan(options, trials);
543
+ const source = prepareEvaluationFixture(options);
544
+ const jobs = prepareTrialParameters(options);
545
+ return { source, jobs, checks: prepareCheckPlan(options, source) };
510
546
  }
@@ -0,0 +1,88 @@
1
+ import { checkpointRunOwner, type RunOwner } from "../run-owner";
2
+ import { summarizeAssessments, type Assessment } from "../results";
3
+ import type { Diagnostic } from "../contract-types";
4
+ import type { RunEvidenceDataTrialEvidence } from "../schema-types";
5
+ import type { CaseSummary, TrialSummary } from "./types";
6
+
7
+ type TrialEvidence = RunEvidenceDataTrialEvidence;
8
+
9
+ export interface CompletedTrial {
10
+ readonly summary: TrialSummary;
11
+ readonly evidence: TrialEvidence;
12
+ readonly diagnostic?: Diagnostic;
13
+ }
14
+
15
+ export interface CollectedResults {
16
+ readonly caseSummary: CaseSummary;
17
+ readonly trials: readonly TrialEvidence[];
18
+ readonly diagnostic: Diagnostic | undefined;
19
+ }
20
+
21
+ /** Registers retained trials before checkpointing; owns their final ordering. */
22
+ export class ResultCollector {
23
+ private readonly summaries: TrialSummary[] = [];
24
+ private readonly evidence: TrialEvidence[] = [];
25
+ private readonly diagnostics: { trial: number; diagnostic: Diagnostic }[] =
26
+ [];
27
+
28
+ constructor(private readonly owner: RunOwner) {}
29
+
30
+ complete(record: CompletedTrial): void {
31
+ this.summaries.push(record.summary);
32
+ this.summaries.sort((left, right) => left.trial - right.trial);
33
+ this.evidence.push(record.evidence);
34
+ this.evidence.sort((left, right) => left.trial - right.trial);
35
+ if (record.diagnostic)
36
+ this.diagnostics.push({
37
+ trial: record.summary.trial,
38
+ diagnostic: record.diagnostic,
39
+ });
40
+ this.checkpoint("active");
41
+ }
42
+
43
+ snapshot(input: {
44
+ caseId: string;
45
+ passThreshold: number;
46
+ cancelled: boolean;
47
+ }): CollectedResults {
48
+ const assessment: Assessment =
49
+ !this.summaries.length && input.cancelled
50
+ ? {
51
+ execution: { status: "cancelled" },
52
+ grading: { status: "not_requested" },
53
+ task: { verdict: "not_assessed" },
54
+ }
55
+ : summarizeAssessments(this.summaries, input.passThreshold);
56
+ const diagnostic = this.diagnostics.sort(
57
+ (left, right) => left.trial - right.trial,
58
+ )[0]?.diagnostic;
59
+ return {
60
+ caseSummary: {
61
+ caseId: input.caseId,
62
+ ...assessment,
63
+ trials: this.summaries,
64
+ },
65
+ trials: this.evidence,
66
+ diagnostic,
67
+ };
68
+ }
69
+
70
+ checkpoint(status: "active" | "complete" | "interrupted"): void {
71
+ checkpointRunOwner(this.owner, this.checkpointTrials(), status);
72
+ }
73
+
74
+ diagnoseFailure(): void {
75
+ try {
76
+ checkpointRunOwner(this.owner, this.checkpointTrials(), "diagnostic");
77
+ } catch {
78
+ /* Preserve the original failure and retained artifacts. */
79
+ }
80
+ }
81
+
82
+ private checkpointTrials() {
83
+ return this.summaries.map((trial) => ({
84
+ trial: trial.trial,
85
+ artifactPath: trial.artifactPath,
86
+ }));
87
+ }
88
+ }
@@ -0,0 +1,10 @@
1
+ import type { ResultCollector } from "./collection";
2
+ import type { PreparedEvaluation } from "./plan";
3
+ import type { PreparationArtifactReference, RunResources } from "./resources";
4
+
5
+ export interface EvaluationContext {
6
+ readonly prepared: PreparedEvaluation;
7
+ readonly resources: RunResources;
8
+ readonly results: ResultCollector;
9
+ readonly preparationArtifacts: readonly PreparationArtifactReference[];
10
+ }
@@ -1,11 +1,11 @@
1
- import { hostArtifacts } from "./evaluation-fixture";
2
- import type { HostResult, ResolvedCase } from "./evaluation-types";
3
- import { runtimeSeedSources, type RuntimePolicy } from "./runtime-config";
1
+ import { hostArtifacts } from "../evidence/host-results";
2
+ import type { HostResult } from "./types";
3
+ import { runtimeSeedSources, type RuntimePolicy } from "../runtime/config";
4
4
  export function scopedHostArtifacts(
5
5
  response: HostResult,
6
6
  prefix: string,
7
- artifacts: { id: string }[],
8
- checks: ResolvedCase["checks"],
7
+ artifacts: readonly { id: string }[],
8
+ checks: readonly { id: string }[],
9
9
  reservedId: string,
10
10
  ) {
11
11
  return hostArtifacts(