@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,49 +1,61 @@
1
- import { randomUUID } from "node:crypto";
2
1
  import { realpath } from "node:fs/promises";
3
2
  import {
4
3
  EvaluationConfigurationError,
5
4
  type EvaluationOptions,
6
5
  type HostAdapter,
7
6
  type ResolvedCase,
8
- } from "./evaluation-types";
9
- import { prepareFixtureSetup, type FixtureSetup } from "./fixture-setup";
10
- import { type GeneratedFixture } from "./generated-fixture";
11
- import { type SemanticCheckDeclaration } from "./graders/semantic";
12
- import { canonicalJson, createEvaluationIdentity, hashJson } from "./identity";
7
+ } from "./types";
8
+ import { prepareFixtureSetup, type FixtureSetup } from "../fixtures/setup";
9
+ import type { FixtureSource } from "../fixtures/create";
10
+ import { type SemanticCheckDeclaration } from "../graders/semantic";
11
+ import { canonicalJson, createEvaluationIdentity, hashJson } from "../identity";
13
12
  import {
14
13
  prepareInstrumentation,
15
14
  type InstrumentationRequest,
16
- } from "./instrumentation";
15
+ } from "../instrumentation";
17
16
  import {
18
17
  fixtureParts,
19
18
  prepareArtifacts,
20
19
  type InlineArtifact,
21
- } from "./preparation";
22
- import { projectProvenance, runnerProvenance } from "./provenance";
20
+ } from "../fixtures/preparation";
21
+ import { projectProvenance, runnerProvenance } from "../provenance";
22
+ import { resolveRepositorySource } from "../fixtures/repository";
23
23
  import {
24
- resolveRepositorySource,
25
- type RepositorySource,
26
- } from "./repository-fixture";
27
- import { prepareEvaluationChecks } from "./evaluation-check-plan";
28
- import { prepareInvocationPlan } from "./evaluation-invocation-plan";
24
+ prepareEvaluationChecks,
25
+ type EvaluationCheckPlan,
26
+ type PreparedChecks,
27
+ } from "./check-plan";
28
+ import {
29
+ prepareInvocationPlan,
30
+ type PreparedInvocation,
31
+ } from "./invocation-plan";
32
+ import type { PreparationResult } from "../extension-session";
33
+
34
+ export type { PreparedChecks } from "./check-plan";
35
+ export type { PreparedInvocation } from "./invocation-plan";
36
+
37
+ interface EnvironmentPlan {
38
+ readonly projectRoot: string;
39
+ readonly source: FixtureSource;
40
+ readonly runner: Awaited<ReturnType<typeof runnerProvenance>>;
41
+ readonly project: Awaited<ReturnType<typeof projectProvenance>>;
42
+ readonly extensionPreparation: PreparationResult | null;
43
+ }
44
+
45
+ interface ArtifactPlan {
46
+ readonly fixtureSetup: FixtureSetup | null;
47
+ readonly requestedInstrumentation: InstrumentationRequest[];
48
+ readonly inlineArtifacts: InlineArtifact[];
49
+ }
29
50
 
30
51
  async function prepareEvaluationEnvironment(
31
52
  options: EvaluationOptions,
32
- state: ReturnType<typeof prepareEvaluationChecks>,
33
- ) {
53
+ source: FixtureSource<null>,
54
+ ): Promise<EnvironmentPlan> {
34
55
  const projectRoot = await realpath(options.projectRoot).catch(() => {
35
56
  throw new EvaluationConfigurationError("project root is unreadable");
36
57
  });
37
- const repository = options.case.fixture.sourceRef
38
- ? await resolveRepositorySource(
39
- options.case.fixture.sourceRef,
40
- options.preparationSources,
41
- ).catch((error: unknown) => {
42
- throw new EvaluationConfigurationError(
43
- error instanceof Error ? error.message : "invalid repository source",
44
- );
45
- })
46
- : null;
58
+ const resolvedSource = await resolveFixtureSource(options, source);
47
59
  const [runner, project] = await Promise.all([
48
60
  runnerProvenance(
49
61
  options.runnerBuildDigest,
@@ -53,7 +65,7 @@ async function prepareEvaluationEnvironment(
53
65
  error instanceof Error ? error.message : "invalid runner provenance",
54
66
  );
55
67
  }),
56
- projectProvenance(projectRoot),
68
+ projectProvenance(projectRoot, evaluationStorageRoots(options)),
57
69
  ]);
58
70
  const extensionPreparation = options.extension
59
71
  ? await options.extension.session.prepare(
@@ -69,16 +81,33 @@ async function prepareEvaluationEnvironment(
69
81
  )
70
82
  : null;
71
83
  return {
72
- ...state,
73
84
  projectRoot,
74
- repository,
85
+ source: resolvedSource,
75
86
  runner,
76
87
  project,
77
88
  extensionPreparation,
78
89
  };
79
90
  }
80
91
 
81
- type EnvironmentPlan = Awaited<ReturnType<typeof prepareEvaluationEnvironment>>;
92
+ async function resolveFixtureSource(
93
+ options: EvaluationOptions,
94
+ source: FixtureSource<null>,
95
+ ): Promise<FixtureSource> {
96
+ if (source.kind !== "repository") return source;
97
+ const repository = await resolveRepositorySource(
98
+ source.fixture.sourceRef,
99
+ options.preparationSources,
100
+ ).catch((error: unknown) => {
101
+ throw new EvaluationConfigurationError(
102
+ error instanceof Error ? error.message : "invalid repository source",
103
+ );
104
+ });
105
+ return { ...source, repository };
106
+ }
107
+
108
+ function evaluationStorageRoots(options: EvaluationOptions): string[] {
109
+ return [options.resultsRoot, options.runStateRoot ?? options.resultsRoot];
110
+ }
82
111
 
83
112
  function preparedFixtureSetup(
84
113
  preparation: EnvironmentPlan["extensionPreparation"],
@@ -110,10 +139,9 @@ function validateSetupCapability(
110
139
 
111
140
  function validateSetupFixture(
112
141
  setup: FixtureSetup | null,
113
- generated: GeneratedFixture | null,
114
- repository: RepositorySource | null,
142
+ source: FixtureSource,
115
143
  ): void {
116
- if (setup && !generated && !repository)
144
+ if (setup && source.kind === "inline")
117
145
  throw new EvaluationConfigurationError(
118
146
  "fixture setup requires a Git fixture",
119
147
  );
@@ -148,17 +176,22 @@ function requestedInstrumentationPlan(
148
176
  }
149
177
  }
150
178
 
151
- function fixturePreparationPaths(
152
- fixture: ResolvedCase["fixture"],
153
- generated: GeneratedFixture | null,
154
- ): string[] {
155
- if (!generated) return Object.keys(fixture.files ?? {});
156
- return [
157
- ...new Set([
158
- ...generated.commits.flatMap((commit) => Object.keys(commit.files)),
159
- ...Object.keys(generated.files ?? {}),
160
- ]),
161
- ];
179
+ function fixturePreparationPaths(source: FixtureSource): string[] {
180
+ switch (source.kind) {
181
+ case "inline":
182
+ return Object.keys(source.files ?? {});
183
+ case "repository":
184
+ return Object.keys(source.fixture.files ?? {});
185
+ case "generated":
186
+ return [
187
+ ...new Set([
188
+ ...source.fixture.commits.flatMap((commit) =>
189
+ Object.keys(commit.files),
190
+ ),
191
+ ...Object.keys(source.fixture.files ?? {}),
192
+ ]),
193
+ ];
194
+ }
162
195
  }
163
196
 
164
197
  async function preparedEvaluationArtifacts(
@@ -168,7 +201,7 @@ async function preparedEvaluationArtifacts(
168
201
  try {
169
202
  return await prepareArtifacts(
170
203
  context.extensionPreparation?.artifacts ?? [],
171
- fixturePreparationPaths(options.case.fixture, context.generated),
204
+ fixturePreparationPaths(context.source),
172
205
  options.preparationSources,
173
206
  );
174
207
  } catch (cause) {
@@ -181,11 +214,10 @@ async function preparedEvaluationArtifacts(
181
214
 
182
215
  function validateArtifactMetadata(
183
216
  artifacts: InlineArtifact[],
184
- generated: GeneratedFixture | null,
185
- repository: RepositorySource | null,
217
+ source: FixtureSource,
186
218
  ): void {
187
219
  if (
188
- (repository || generated) &&
220
+ source.kind !== "inline" &&
189
221
  artifacts.some((artifact) =>
190
222
  fixtureParts(artifact.relativePath).some(
191
223
  (part) => part.toLowerCase() === ".git",
@@ -199,12 +231,10 @@ function validateArtifactMetadata(
199
231
 
200
232
  function validateArtifactExclusions(
201
233
  artifacts: InlineArtifact[],
202
- generated: GeneratedFixture | null,
203
- repository: RepositorySource | null,
234
+ source: FixtureSource,
204
235
  ): void {
205
236
  if (
206
- !repository &&
207
- !generated &&
237
+ source.kind === "inline" &&
208
238
  artifacts.some((artifact) => artifact.gitExclude)
209
239
  )
210
240
  throw new EvaluationConfigurationError(
@@ -228,27 +258,25 @@ function validateSemanticArtifactId(
228
258
  async function prepareEvaluationArtifacts(
229
259
  options: EvaluationOptions,
230
260
  state: EnvironmentPlan,
231
- ) {
261
+ checks: EvaluationCheckPlan,
262
+ ): Promise<ArtifactPlan> {
232
263
  const fixtureSetup = preparedFixtureSetup(state.extensionPreparation);
233
264
  validateSetupCapability(options, fixtureSetup);
234
- validateSetupFixture(fixtureSetup, state.generated, state.repository);
265
+ validateSetupFixture(fixtureSetup, state.source);
235
266
  const requestedInstrumentation = requestedInstrumentationPlan(
236
267
  options,
237
268
  state.extensionPreparation,
238
269
  );
239
270
  const inlineArtifacts = await preparedEvaluationArtifacts(options, state);
240
- validateArtifactMetadata(inlineArtifacts, state.generated, state.repository);
241
- validateArtifactExclusions(
271
+ validateArtifactMetadata(inlineArtifacts, state.source);
272
+ validateArtifactExclusions(inlineArtifacts, state.source);
273
+ validateSemanticArtifactId(
242
274
  inlineArtifacts,
243
- state.generated,
244
- state.repository,
275
+ checks.checks.semanticDeclarations,
245
276
  );
246
- validateSemanticArtifactId(inlineArtifacts, state.semanticDeclarations);
247
- return { ...state, fixtureSetup, requestedInstrumentation, inlineArtifacts };
277
+ return { fixtureSetup, requestedInstrumentation, inlineArtifacts };
248
278
  }
249
279
 
250
- type ArtifactPlan = Awaited<ReturnType<typeof prepareEvaluationArtifacts>>;
251
- type InvocationPlan = ReturnType<typeof prepareInvocationPlan<ArtifactPlan>>;
252
280
  type EvaluationRouteRole = "candidate" | "semantic" | "advisory";
253
281
  interface ActiveGrader {
254
282
  id: string;
@@ -256,9 +284,35 @@ interface ActiveGrader {
256
284
  version: string;
257
285
  }
258
286
 
287
+ export interface PreparedFixture {
288
+ readonly projectRoot: string;
289
+ readonly source: FixtureSource;
290
+ readonly fixtureSetup: ArtifactPlan["fixtureSetup"];
291
+ readonly inlineArtifacts: InlineArtifact[];
292
+ }
293
+
294
+ interface PreparedEvaluationInputs {
295
+ readonly fixture: PreparedFixture;
296
+ readonly checks: PreparedChecks;
297
+ readonly invocation: PreparedInvocation;
298
+ readonly jobs: number;
299
+ readonly requestedInstrumentation: InstrumentationRequest[];
300
+ readonly runner: EnvironmentPlan["runner"];
301
+ readonly project: EnvironmentPlan["project"];
302
+ }
303
+
304
+ export interface PreparedEvaluation extends PreparedEvaluationInputs {
305
+ readonly extensionData: Record<string, unknown>;
306
+ readonly route: ReturnType<typeof evaluationRoute>;
307
+ readonly routes: ReturnType<typeof evaluationRoutes>;
308
+ readonly redactedConfig: ReturnType<typeof redactedEvaluationConfiguration>;
309
+ readonly activeGraders: ActiveGrader[];
310
+ readonly evaluationIdentity: ReturnType<typeof createEvaluationIdentity>;
311
+ }
312
+
259
313
  function extensionPreparationData(
260
314
  options: EvaluationOptions,
261
- preparation: InvocationPlan["extensionPreparation"],
315
+ preparation: EnvironmentPlan["extensionPreparation"],
262
316
  ): Record<string, unknown> {
263
317
  return options.extension
264
318
  ? {
@@ -327,36 +381,16 @@ function redactedExtensionConfiguration(options: EvaluationOptions) {
327
381
  };
328
382
  }
329
383
 
330
- function skillInvocationConfiguration(state: InvocationPlan) {
384
+ function redactedPreparationConfiguration(state: PreparedEvaluationInputs) {
331
385
  return {
332
- ...(state.codexSkillInvocation
333
- ? { codexSkillInvocation: state.codexSkillInvocation }
386
+ ...(state.fixture.fixtureSetup
387
+ ? { fixtureSetupDigest: hashJson(state.fixture.fixtureSetup) }
334
388
  : {}),
335
- ...(state.codexRepositorySkillInvocation
336
- ? { codexRepositorySkillInvocation: state.codexRepositorySkillInvocation }
389
+ ...(state.invocation.codexMarketplace
390
+ ? { codexMarketplace: state.invocation.codexMarketplace }
337
391
  : {}),
338
- ...(state.claudeSkillInvocation
339
- ? { claudeSkillInvocation: state.claudeSkillInvocation }
340
- : {}),
341
- ...(state.claudeRepositorySkillInvocation
342
- ? {
343
- claudeRepositorySkillInvocation:
344
- state.claudeRepositorySkillInvocation,
345
- }
346
- : {}),
347
- };
348
- }
349
-
350
- function redactedPreparationConfiguration(state: InvocationPlan) {
351
- return {
352
- ...(state.fixtureSetup
353
- ? { fixtureSetupDigest: hashJson(state.fixtureSetup) }
354
- : {}),
355
- ...(state.codexMarketplace
356
- ? { codexMarketplace: state.codexMarketplace }
357
- : {}),
358
- ...(state.claudePluginDirs
359
- ? { claudePluginDirs: state.claudePluginDirs }
392
+ ...(state.invocation.claudePluginDirs
393
+ ? { claudePluginDirs: state.invocation.claudePluginDirs }
360
394
  : {}),
361
395
  };
362
396
  }
@@ -373,7 +407,7 @@ function redactedAdvisoryConfiguration(options: EvaluationOptions) {
373
407
 
374
408
  function redactedEvaluationConfiguration(
375
409
  options: EvaluationOptions,
376
- state: InvocationPlan,
410
+ state: PreparedEvaluationInputs,
377
411
  ) {
378
412
  return {
379
413
  condition: options.condition,
@@ -382,17 +416,23 @@ function redactedEvaluationConfiguration(
382
416
  trialCount: options.trialCount,
383
417
  jobs: state.jobs,
384
418
  passThreshold: options.passThreshold,
385
- ...redactedHostConfiguration(options, state.preparedSemantic.length > 0),
419
+ ...(options.assessmentSettings
420
+ ? { runSettings: options.assessmentSettings }
421
+ : {}),
422
+ ...redactedHostConfiguration(
423
+ options,
424
+ state.checks.preparedSemantic.length > 0,
425
+ ),
386
426
  ...redactedExtensionConfiguration(options),
387
427
  ...redactedPreparationConfiguration(state),
388
- ...skillInvocationConfiguration(state),
428
+ ...state.invocation.configuration,
389
429
  ...redactedAdvisoryConfiguration(options),
390
430
  };
391
431
  }
392
432
 
393
433
  function activeEvaluationGraders(
394
434
  options: EvaluationOptions,
395
- state: InvocationPlan,
435
+ state: PreparedChecks,
396
436
  ): ActiveGrader[] {
397
437
  const graders: ActiveGrader[] = [
398
438
  ...new Set(
@@ -440,14 +480,15 @@ function caseIdentityData(
440
480
  };
441
481
  }
442
482
 
443
- function baseFixtureIdentity(
444
- state: InvocationPlan,
445
- fixture: ResolvedCase["fixture"],
446
- ) {
447
- if (state.generated) return state.generated;
448
- if (state.repository)
449
- return { ...state.repositoryFixture, revision: state.repository.revision };
450
- return { files: fixture.files };
483
+ function baseFixtureIdentity(source: FixtureSource) {
484
+ switch (source.kind) {
485
+ case "generated":
486
+ return source.fixture;
487
+ case "repository":
488
+ return { ...source.fixture, revision: source.repository.revision };
489
+ case "inline":
490
+ return { files: source.files };
491
+ }
451
492
  }
452
493
 
453
494
  function artifactIdentityData(artifacts: InlineArtifact[]) {
@@ -462,21 +503,20 @@ function artifactIdentityData(artifacts: InlineArtifact[]) {
462
503
  );
463
504
  }
464
505
 
465
- function fixtureIdentityData(
466
- state: InvocationPlan,
467
- fixture: ResolvedCase["fixture"],
468
- ) {
506
+ function fixtureIdentityData(state: PreparedEvaluationInputs) {
469
507
  return {
470
- ...baseFixtureIdentity(state, fixture),
471
- artifacts: artifactIdentityData(state.inlineArtifacts),
472
- ...(state.fixtureSetup ? { fixtureSetup: state.fixtureSetup } : {}),
473
- ...(state.codexMarketplace
474
- ? { codexMarketplace: state.codexMarketplace }
508
+ ...baseFixtureIdentity(state.fixture.source),
509
+ artifacts: artifactIdentityData(state.fixture.inlineArtifacts),
510
+ ...(state.fixture.fixtureSetup
511
+ ? { fixtureSetup: state.fixture.fixtureSetup }
512
+ : {}),
513
+ ...(state.invocation.codexMarketplace
514
+ ? { codexMarketplace: state.invocation.codexMarketplace }
475
515
  : {}),
476
- ...(state.claudePluginDirs
477
- ? { claudePluginDirs: state.claudePluginDirs }
516
+ ...(state.invocation.claudePluginDirs
517
+ ? { claudePluginDirs: state.invocation.claudePluginDirs }
478
518
  : {}),
479
- ...skillInvocationConfiguration(state),
519
+ ...state.invocation.configuration,
480
520
  };
481
521
  }
482
522
 
@@ -491,7 +531,7 @@ function evaluatorIdentityData(
491
531
  };
492
532
  }
493
533
 
494
- type IdentityPlan = InvocationPlan & {
534
+ type IdentityMetadata = {
495
535
  extensionData: Record<string, unknown>;
496
536
  route: ReturnType<typeof evaluationRoute>;
497
537
  routes: ReturnType<typeof evaluationRoutes>;
@@ -501,7 +541,8 @@ type IdentityPlan = InvocationPlan & {
501
541
 
502
542
  function evaluationComparisonIdentity(
503
543
  options: EvaluationOptions,
504
- context: IdentityPlan,
544
+ prepared: PreparedEvaluationInputs,
545
+ context: IdentityMetadata,
505
546
  ) {
506
547
  try {
507
548
  return createEvaluationIdentity({
@@ -512,9 +553,7 @@ function evaluationComparisonIdentity(
512
553
  caseDigest: hashJson(
513
554
  caseIdentityData(options.case, context.extensionData),
514
555
  ),
515
- fixtureDigest: hashJson(
516
- fixtureIdentityData(context, options.case.fixture),
517
- ),
556
+ fixtureDigest: hashJson(fixtureIdentityData(prepared)),
518
557
  checksDigest: hashJson(options.case.checks),
519
558
  requiredEvidenceDigest: hashJson(options.case.requiredEvidence),
520
559
  evaluatorDigest: hashJson(
@@ -522,8 +561,8 @@ function evaluationComparisonIdentity(
522
561
  ),
523
562
  graderDigest: hashJson(context.activeGraders),
524
563
  instrumentationDigest: hashJson({
525
- requested: context.requestedInstrumentation,
526
- applied: context.requestedInstrumentation,
564
+ requested: prepared.requestedInstrumentation,
565
+ applied: prepared.requestedInstrumentation,
527
566
  }),
528
567
  routeDigest: hashJson(
529
568
  context.routes.length > 1 ? context.routes : context.route,
@@ -542,36 +581,36 @@ function evaluationComparisonIdentity(
542
581
 
543
582
  function prepareEvaluationIdentity(
544
583
  options: EvaluationOptions,
545
- state: InvocationPlan,
546
- ) {
547
- const extensionData = extensionPreparationData(
548
- options,
549
- state.extensionPreparation,
550
- );
551
- const runId = randomUUID();
584
+ state: PreparedEvaluationInputs,
585
+ preparation: EnvironmentPlan["extensionPreparation"],
586
+ ): PreparedEvaluation {
587
+ const extensionData = extensionPreparationData(options, preparation);
552
588
  const route = evaluationRoute(options.host, "candidate");
553
589
  const routes = evaluationRoutes(
554
590
  options,
555
- state.preparedSemantic.length > 0,
591
+ state.checks.preparedSemantic.length > 0,
556
592
  route,
557
593
  );
558
594
  const redactedConfig = snapshotEvaluationConfiguration(options, state);
559
- const activeGraders = activeEvaluationGraders(options, state);
595
+ const activeGraders = activeEvaluationGraders(options, state.checks);
560
596
  const context = {
561
- ...state,
562
597
  extensionData,
563
598
  route,
564
599
  routes,
565
600
  redactedConfig,
566
601
  activeGraders,
567
602
  };
568
- const evaluationIdentity = evaluationComparisonIdentity(options, context);
569
- return { ...context, runId, evaluationIdentity };
603
+ const evaluationIdentity = evaluationComparisonIdentity(
604
+ options,
605
+ state,
606
+ context,
607
+ );
608
+ return { ...state, ...context, evaluationIdentity };
570
609
  }
571
610
 
572
611
  function snapshotEvaluationConfiguration(
573
612
  options: EvaluationOptions,
574
- state: InvocationPlan,
613
+ state: PreparedEvaluationInputs,
575
614
  ): ReturnType<typeof redactedEvaluationConfiguration> {
576
615
  try {
577
616
  return JSON.parse(
@@ -585,12 +624,40 @@ function snapshotEvaluationConfiguration(
585
624
  }
586
625
  }
587
626
 
588
- export async function prepareEvaluation(options: EvaluationOptions) {
627
+ export async function prepareEvaluation(
628
+ options: EvaluationOptions,
629
+ ): Promise<PreparedEvaluation> {
589
630
  const checks = prepareEvaluationChecks(options);
590
- const environment = await prepareEvaluationEnvironment(options, checks);
591
- const artifacts = await prepareEvaluationArtifacts(options, environment);
592
- const invocation = prepareInvocationPlan(options, artifacts);
593
- return prepareEvaluationIdentity(options, invocation);
631
+ const environment = await prepareEvaluationEnvironment(
632
+ options,
633
+ checks.source,
634
+ );
635
+ const artifacts = await prepareEvaluationArtifacts(
636
+ options,
637
+ environment,
638
+ checks,
639
+ );
640
+ const invocation = prepareInvocationPlan(options, {
641
+ extensionPreparation: environment.extensionPreparation,
642
+ inlineArtifacts: artifacts.inlineArtifacts,
643
+ });
644
+ const prepared: PreparedEvaluationInputs = {
645
+ fixture: {
646
+ projectRoot: environment.projectRoot,
647
+ source: environment.source,
648
+ fixtureSetup: artifacts.fixtureSetup,
649
+ inlineArtifacts: artifacts.inlineArtifacts,
650
+ },
651
+ checks: checks.checks,
652
+ invocation,
653
+ jobs: checks.jobs,
654
+ requestedInstrumentation: artifacts.requestedInstrumentation,
655
+ runner: environment.runner,
656
+ project: environment.project,
657
+ };
658
+ return prepareEvaluationIdentity(
659
+ options,
660
+ prepared,
661
+ environment.extensionPreparation,
662
+ );
594
663
  }
595
-
596
- export type EvaluationPlan = Awaited<ReturnType<typeof prepareEvaluation>>;
@@ -0,0 +1,19 @@
1
+ import type { EvaluationOptions, EvaluationProgress } from "./types";
2
+
3
+ /** Observation is best-effort and cannot change assessment or persistence. */
4
+ export function evaluationProgress(
5
+ options: EvaluationOptions,
6
+ stage: EvaluationProgress["stage"],
7
+ status: EvaluationProgress["status"],
8
+ trial?: number,
9
+ ): void {
10
+ try {
11
+ options.onProgress?.({
12
+ stage,
13
+ status,
14
+ ...(trial === undefined ? {} : { trial }),
15
+ });
16
+ } catch {
17
+ // An observer has no authority over the evaluation lifecycle.
18
+ }
19
+ }