@bjoernrochel/sevro 0.1.0-rc.2 → 0.1.0-rc.21

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 (243) hide show
  1. package/CONTRIBUTING.md +170 -0
  2. package/LICENSE +63 -136
  3. package/README.md +83 -66
  4. package/docs/README.md +44 -0
  5. package/docs/acknowledgements.md +14 -0
  6. package/docs/architecture.md +146 -0
  7. package/docs/assets/README.md +27 -0
  8. package/docs/assets/sevro-logo.png +0 -0
  9. package/docs/assets/sevro-symbol.svg +1 -0
  10. package/docs/assets/sevro-wordmark.svg +1 -0
  11. package/docs/builtin-graders-v1.md +41 -13
  12. package/docs/codex-host.md +184 -52
  13. package/docs/contracts.md +19 -0
  14. package/docs/creating-cases.md +139 -0
  15. package/docs/development-cli.md +190 -30
  16. package/docs/documentation-quality.md +61 -0
  17. package/docs/evaluation-workflow.md +110 -0
  18. package/docs/evidence/code-mode-controls-71.md +64 -0
  19. package/docs/evidence/code-mode-programs-85.md +64 -0
  20. package/docs/evidence/codex-state-73/README.md +204 -0
  21. package/docs/evidence/codex-state-73/darrow-migration.patch +182 -0
  22. package/docs/evidence/codex-state-73/results.json +661 -0
  23. package/docs/evidence/local-http-74.md +104 -0
  24. package/docs/evidence/runtime-startup-30/README.md +185 -0
  25. package/docs/evidence/runtime-startup-30/after.json +319 -0
  26. package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +2 -0
  27. package/docs/evidence/runtime-startup-30/before.json +403 -0
  28. package/docs/evidence/runtime-startup-30/consumer-smoke.json +63 -0
  29. package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +3 -0
  30. package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +3 -0
  31. package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +20 -0
  32. package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +2 -0
  33. package/docs/evidence/skill-reads-70/results.json +823 -0
  34. package/docs/evidence.md +21 -0
  35. package/docs/extension-protocol-v1.md +50 -16
  36. package/docs/fixture-cleanup-validation.md +3 -0
  37. package/docs/getting-started.md +102 -0
  38. package/docs/guide-evaluation.md +89 -0
  39. package/docs/guide-verification.json +239 -0
  40. package/docs/identity-v1.md +35 -1
  41. package/docs/installing.md +88 -0
  42. package/docs/licenses/BUSL-1.1-0.1.0-rc.2.txt +159 -0
  43. package/docs/licensing.md +42 -0
  44. package/docs/native-controls.md +76 -5
  45. package/docs/native-hosts.md +228 -0
  46. package/docs/reading-results.md +141 -0
  47. package/docs/recipes.md +489 -0
  48. package/docs/releases.md +81 -40
  49. package/docs/report-v1.md +29 -1
  50. package/docs/results-v1.md +180 -0
  51. package/docs/run-settings-v1.md +147 -0
  52. package/docs/running-evaluations.md +31 -0
  53. package/docs/runtime-v1.md +614 -0
  54. package/docs/specs/repository-guide.md +58 -0
  55. package/docs/troubleshooting.md +30 -0
  56. package/docs/typescript-quality.md +234 -0
  57. package/docs/usability-study.md +114 -0
  58. package/examples/basic/host.ts +2 -2
  59. package/examples/recipes/candidate.ts +26 -0
  60. package/examples/recipes/cases/coding-change.json +21 -0
  61. package/examples/recipes/cases/host-tools.json +21 -0
  62. package/examples/recipes/cases/mise.json +21 -0
  63. package/examples/recipes/cases/node.json +21 -0
  64. package/examples/recipes/cases/python-uv.json +21 -0
  65. package/examples/recipes/cases/semantic.json +17 -0
  66. package/examples/recipes/coding-change/check.ts +23 -0
  67. package/examples/recipes/extension-artifacts.ts +65 -0
  68. package/examples/recipes/extension-methods.ts +174 -0
  69. package/examples/recipes/extension.ts +37 -0
  70. package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
  71. package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
  72. package/examples/recipes/repository-skill/README.md +3 -0
  73. package/examples/recipes/run-inputs.ts +188 -0
  74. package/examples/recipes/run-options.ts +131 -0
  75. package/examples/recipes/run.ts +63 -0
  76. package/examples/recipes/runtime/empty.json +1 -0
  77. package/examples/recipes/runtime/goals.json +1 -0
  78. package/examples/recipes/runtime/mise.json +6 -0
  79. package/examples/recipes/runtime/path.json +1 -0
  80. package/examples/recipes/runtime/plugins.json +4 -0
  81. package/examples/recipes/runtime/python-uv.json +10 -0
  82. package/examples/recipes/semantic-judge.ts +29 -0
  83. package/examples/templates/coding/adapter.ts +17 -0
  84. package/examples/templates/coding/case.json +15 -0
  85. package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
  86. package/examples/templates/repository-skill/extension-data.ts +64 -0
  87. package/examples/templates/repository-skill/extension.ts +31 -0
  88. package/examples/templates/response/adapter.ts +12 -0
  89. package/examples/templates/response/case.json +13 -0
  90. package/package.json +39 -6
  91. package/schemas/report-v1.schema.json +9 -0
  92. package/schemas/run-evidence-v1.schema.json +2 -1
  93. package/schemas/runtime-v1.schema.json +193 -0
  94. package/src/advisory.ts +10 -5
  95. package/src/candidate-artifacts.ts +208 -0
  96. package/src/candidate-codex-state.ts +185 -0
  97. package/src/case-validation.ts +105 -0
  98. package/src/cli/cancellation.ts +20 -0
  99. package/src/cli/command.ts +81 -0
  100. package/src/cli/completion.ts +15 -0
  101. package/src/cli/demo.ts +59 -0
  102. package/src/cli/evaluation.ts +55 -0
  103. package/src/cli/extension.ts +79 -0
  104. package/src/cli/help.ts +222 -0
  105. package/src/cli/hosts.ts +59 -0
  106. package/src/cli/init-settings.ts +30 -0
  107. package/src/cli/init.ts +111 -0
  108. package/src/cli/invocation.ts +627 -0
  109. package/src/cli/loading.ts +151 -0
  110. package/src/cli/preparation.ts +163 -0
  111. package/src/cli/presentation.ts +91 -0
  112. package/src/cli/progress.ts +6 -0
  113. package/src/cli/run-manifest.ts +133 -0
  114. package/src/cli/run-options.ts +101 -0
  115. package/src/cli/run-overrides.ts +83 -0
  116. package/src/cli/settings.ts +160 -0
  117. package/src/cli/summary.ts +43 -0
  118. package/src/cli.ts +13 -843
  119. package/src/contract-types.ts +94 -0
  120. package/src/engine.ts +19 -2373
  121. package/src/evaluation/advisory.ts +150 -0
  122. package/src/evaluation/check-plan.ts +510 -0
  123. package/src/evaluation/collection.ts +88 -0
  124. package/src/evaluation/context.ts +10 -0
  125. package/src/evaluation/grading-host.ts +39 -0
  126. package/src/evaluation/grading.ts +258 -0
  127. package/src/evaluation/invocation-plan.ts +548 -0
  128. package/src/evaluation/plan.ts +736 -0
  129. package/src/evaluation/progress.ts +19 -0
  130. package/src/evaluation/resources.ts +241 -0
  131. package/src/evaluation/run.ts +206 -0
  132. package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +3 -1
  133. package/src/evaluation/semantic.ts +312 -0
  134. package/src/evaluation/trial.ts +866 -0
  135. package/src/evaluation/types.ts +207 -0
  136. package/src/evidence/host-results.ts +92 -0
  137. package/src/evidence/retained-artifacts.ts +18 -0
  138. package/src/evidence/trial-writer.ts +203 -0
  139. package/src/evidence/usage.ts +17 -0
  140. package/src/extension-client.ts +228 -148
  141. package/src/extension-session.ts +326 -195
  142. package/src/fixtures/advisory.ts +338 -0
  143. package/src/fixtures/bin.ts +57 -0
  144. package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
  145. package/src/fixtures/create.ts +187 -0
  146. package/src/fixtures/generated.ts +300 -0
  147. package/src/fixtures/git-hooks.ts +24 -0
  148. package/src/fixtures/preparation.ts +270 -0
  149. package/src/{repository-fixture.ts → fixtures/repository.ts} +207 -85
  150. package/src/fixtures/setup.ts +224 -0
  151. package/src/fixtures/tools.ts +88 -0
  152. package/src/fixtures/workspace-state.ts +212 -0
  153. package/src/generated/cli-result.cjs +2 -0
  154. package/src/generated/cli-result.d.cts +3 -0
  155. package/src/generated/extension.cjs +2 -0
  156. package/src/generated/extension.d.cts +3 -0
  157. package/src/generated/report.cjs +2 -0
  158. package/src/generated/report.d.cts +3 -0
  159. package/src/generated/run-evidence.cjs +2 -0
  160. package/src/generated/run-evidence.d.cts +3 -0
  161. package/src/generated/runtime.cjs +2 -0
  162. package/src/generated/runtime.d.cts +3 -0
  163. package/src/graders/artifact.ts +56 -23
  164. package/src/graders/git-head.ts +79 -75
  165. package/src/graders/output.ts +222 -140
  166. package/src/graders/semantic.ts +102 -43
  167. package/src/graders/shell.ts +379 -207
  168. package/src/host-diagnostic-redaction.ts +52 -0
  169. package/src/host-diagnostics.ts +319 -0
  170. package/src/hosts/claude-continuation.ts +63 -18
  171. package/src/hosts/claude-credential.ts +43 -62
  172. package/src/hosts/claude-events.ts +86 -51
  173. package/src/hosts/claude-keychain.ts +59 -0
  174. package/src/hosts/claude-native-goal.ts +57 -33
  175. package/src/hosts/claude-process.ts +111 -0
  176. package/src/hosts/claude-repository-invocation.ts +208 -117
  177. package/src/hosts/claude-settings.ts +240 -21
  178. package/src/hosts/claude-skill-activations.ts +166 -0
  179. package/src/hosts/claude-state.ts +403 -0
  180. package/src/hosts/claude-tool-calls.ts +171 -106
  181. package/src/hosts/claude.ts +561 -451
  182. package/src/hosts/codex-app-server.ts +326 -178
  183. package/src/hosts/codex-configuration.ts +44 -18
  184. package/src/hosts/codex-events.ts +97 -60
  185. package/src/hosts/codex-exec-controls.ts +516 -0
  186. package/src/hosts/codex-invocation.ts +70 -0
  187. package/src/hosts/codex-marketplace.ts +283 -0
  188. package/src/hosts/codex-native-calls.ts +546 -500
  189. package/src/hosts/codex-process.ts +202 -0
  190. package/src/hosts/codex-profile.ts +257 -28
  191. package/src/hosts/codex-session-files.ts +106 -0
  192. package/src/hosts/codex-state.ts +492 -0
  193. package/src/hosts/codex.ts +783 -832
  194. package/src/hosts/isolation-common.ts +94 -0
  195. package/src/hosts/isolation-roots.ts +54 -27
  196. package/src/hosts/isolation.ts +10 -0
  197. package/src/hosts/linux-sandbox.ts +159 -0
  198. package/src/hosts/mac-sandbox.ts +187 -61
  199. package/src/hosts/native-controls.ts +152 -62
  200. package/src/hosts/process-lifecycle.ts +37 -0
  201. package/src/hosts/protected-worktrees.ts +51 -19
  202. package/src/hosts/skill-hook-catalog.ts +131 -0
  203. package/src/hosts/skill-hook-handler.ts +374 -0
  204. package/src/hosts/skill-hooks.ts +580 -0
  205. package/src/hosts/workspace-fingerprint.ts +55 -35
  206. package/src/identity.ts +102 -48
  207. package/src/instrumentation.ts +109 -48
  208. package/src/native-transcript-state.ts +131 -0
  209. package/src/native-transcript-view.ts +90 -0
  210. package/src/native-transcripts.ts +286 -0
  211. package/src/process-lifecycle.ts +150 -0
  212. package/src/provenance.ts +161 -81
  213. package/src/prune.ts +215 -0
  214. package/src/report.ts +172 -140
  215. package/src/resolved-case.ts +33 -0
  216. package/src/results-lock.ts +29 -0
  217. package/src/results.ts +133 -86
  218. package/src/run-owner.ts +154 -63
  219. package/src/runtime/config.ts +456 -0
  220. package/src/runtime/discovery-apple.ts +90 -0
  221. package/src/runtime/discovery-providers.ts +63 -0
  222. package/src/runtime/discovery.ts +70 -0
  223. package/src/runtime/hook-processes.ts +97 -0
  224. package/src/runtime/hooks.ts +476 -0
  225. package/src/runtime/paths.ts +313 -0
  226. package/src/runtime/query.ts +85 -0
  227. package/src/runtime/seeds.ts +134 -0
  228. package/src/runtime/shared-cache.ts +143 -0
  229. package/src/runtime/state.ts +143 -0
  230. package/src/schema-types.ts +198 -0
  231. package/src/schema.ts +15 -19
  232. package/src/transcript-retention.ts +146 -0
  233. package/src/value-guards.ts +20 -0
  234. package/src/advisory-fixture.ts +0 -282
  235. package/src/fixture-bin.ts +0 -87
  236. package/src/fixture-setup.ts +0 -163
  237. package/src/generated-fixture.ts +0 -220
  238. package/src/git-hooks.ts +0 -63
  239. package/src/hosts/claude-nested-skills.ts +0 -218
  240. package/src/hosts/codex-command-output.ts +0 -399
  241. package/src/hosts/codex-executor-command.ts +0 -195
  242. package/src/hosts/codex-skill-reads.ts +0 -391
  243. package/src/preparation.ts +0 -211
@@ -0,0 +1,736 @@
1
+ import { realpath } from "node:fs/promises";
2
+ import {
3
+ EvaluationConfigurationError,
4
+ type EvaluationOptions,
5
+ type HostAdapter,
6
+ type ResolvedCase,
7
+ } from "./types";
8
+ import { prepareFixtureSetup, type FixtureSetup } from "../fixtures/setup";
9
+ import { type GeneratedFixture } from "../fixtures/generated";
10
+ import { type SemanticCheckDeclaration } from "../graders/semantic";
11
+ import { canonicalJson, createEvaluationIdentity, hashJson } from "../identity";
12
+ import {
13
+ prepareInstrumentation,
14
+ type InstrumentationRequest,
15
+ } from "../instrumentation";
16
+ import {
17
+ fixtureParts,
18
+ prepareArtifacts,
19
+ type InlineArtifact,
20
+ } from "../fixtures/preparation";
21
+ import { projectProvenance, runnerProvenance } from "../provenance";
22
+ import {
23
+ resolveRepositorySource,
24
+ type RepositorySource,
25
+ } from "../fixtures/repository";
26
+ import { prepareEvaluationChecks } from "./check-plan";
27
+ import { prepareInvocationPlan } from "./invocation-plan";
28
+
29
+ async function prepareEvaluationEnvironment(options: EvaluationOptions) {
30
+ const projectRoot = await realpath(options.projectRoot).catch(() => {
31
+ throw new EvaluationConfigurationError("project root is unreadable");
32
+ });
33
+ const repository = options.case.fixture.sourceRef
34
+ ? await resolveRepositorySource(
35
+ options.case.fixture.sourceRef,
36
+ options.preparationSources,
37
+ ).catch((error: unknown) => {
38
+ throw new EvaluationConfigurationError(
39
+ error instanceof Error ? error.message : "invalid repository source",
40
+ );
41
+ })
42
+ : null;
43
+ const [runner, project] = await Promise.all([
44
+ runnerProvenance(
45
+ options.runnerBuildDigest,
46
+ options.runnerCheckoutRoot,
47
+ ).catch((error: unknown) => {
48
+ throw new EvaluationConfigurationError(
49
+ error instanceof Error ? error.message : "invalid runner provenance",
50
+ );
51
+ }),
52
+ projectProvenance(projectRoot, evaluationStorageRoots(options)),
53
+ ]);
54
+ const extensionPreparation = options.extension
55
+ ? await options.extension.session.prepare(
56
+ options.extension.resolvedCase,
57
+ {
58
+ id: options.host.id,
59
+ capabilities: [
60
+ ...(options.host.instrumentation ?? []).map((item) => item.id),
61
+ ...(options.host.hostCapabilities ?? []),
62
+ ],
63
+ },
64
+ options.condition,
65
+ )
66
+ : null;
67
+ return {
68
+ projectRoot,
69
+ repository,
70
+ runner,
71
+ project,
72
+ extensionPreparation,
73
+ };
74
+ }
75
+
76
+ type EnvironmentPlan = Awaited<ReturnType<typeof prepareEvaluationEnvironment>>;
77
+
78
+ function evaluationStorageRoots(options: EvaluationOptions): string[] {
79
+ return [options.resultsRoot, options.runStateRoot ?? options.resultsRoot];
80
+ }
81
+
82
+ function preparedFixtureSetup(
83
+ preparation: EnvironmentPlan["extensionPreparation"],
84
+ ): FixtureSetup | null {
85
+ try {
86
+ return prepareFixtureSetup(preparation?.fixtureSetup);
87
+ } catch (cause) {
88
+ throw new EvaluationConfigurationError(
89
+ cause instanceof Error ? cause.message : "invalid fixture setup",
90
+ { cause },
91
+ );
92
+ }
93
+ }
94
+
95
+ function validateSetupCapability(
96
+ options: EvaluationOptions,
97
+ setup: FixtureSetup | null,
98
+ ): void {
99
+ if (
100
+ setup &&
101
+ !options.extension?.session.identity.capabilities.includes(
102
+ "sevro.fixture.setup",
103
+ )
104
+ )
105
+ throw new EvaluationConfigurationError(
106
+ "fixture setup capability was not negotiated",
107
+ );
108
+ }
109
+
110
+ function validateSetupFixture(
111
+ setup: FixtureSetup | null,
112
+ generated: GeneratedFixture | null,
113
+ repository: RepositorySource | null,
114
+ ): void {
115
+ if (setup && !generated && !repository)
116
+ throw new EvaluationConfigurationError(
117
+ "fixture setup requires a Git fixture",
118
+ );
119
+ }
120
+
121
+ function negotiatedInstrumentationCapabilities(
122
+ options: EvaluationOptions,
123
+ ): string[] {
124
+ return options.extension?.session.identity.capabilities ?? [];
125
+ }
126
+
127
+ function configurationFailureMessage(cause: unknown, fallback: string): string {
128
+ return cause instanceof Error ? cause.message : fallback;
129
+ }
130
+
131
+ function requestedInstrumentationPlan(
132
+ options: EvaluationOptions,
133
+ preparation: EnvironmentPlan["extensionPreparation"],
134
+ ): InstrumentationRequest[] {
135
+ try {
136
+ return prepareInstrumentation(
137
+ preparation?.requestedInstrumentation ?? [],
138
+ options.host.instrumentation ?? [],
139
+ negotiatedInstrumentationCapabilities(options),
140
+ options.condition,
141
+ );
142
+ } catch (cause) {
143
+ throw new EvaluationConfigurationError(
144
+ configurationFailureMessage(cause, "invalid instrumentation"),
145
+ { cause },
146
+ );
147
+ }
148
+ }
149
+
150
+ function fixturePreparationPaths(
151
+ fixture: ResolvedCase["fixture"],
152
+ generated: GeneratedFixture | null,
153
+ ): string[] {
154
+ if (!generated) return Object.keys(fixture.files ?? {});
155
+ return [
156
+ ...new Set([
157
+ ...generated.commits.flatMap((commit) => Object.keys(commit.files)),
158
+ ...Object.keys(generated.files ?? {}),
159
+ ]),
160
+ ];
161
+ }
162
+
163
+ async function preparedEvaluationArtifacts(
164
+ options: EvaluationOptions,
165
+ context: EnvironmentPlan,
166
+ generated: GeneratedFixture | null,
167
+ ): Promise<InlineArtifact[]> {
168
+ try {
169
+ return await prepareArtifacts(
170
+ context.extensionPreparation?.artifacts ?? [],
171
+ fixturePreparationPaths(options.case.fixture, generated),
172
+ options.preparationSources,
173
+ );
174
+ } catch (cause) {
175
+ throw new EvaluationConfigurationError(
176
+ cause instanceof Error ? cause.message : "invalid preparation artifacts",
177
+ { cause },
178
+ );
179
+ }
180
+ }
181
+
182
+ function validateArtifactMetadata(
183
+ artifacts: InlineArtifact[],
184
+ generated: GeneratedFixture | null,
185
+ repository: RepositorySource | null,
186
+ ): void {
187
+ if (
188
+ (repository || generated) &&
189
+ artifacts.some((artifact) =>
190
+ fixtureParts(artifact.relativePath).some(
191
+ (part) => part.toLowerCase() === ".git",
192
+ ),
193
+ )
194
+ )
195
+ throw new EvaluationConfigurationError(
196
+ "preparation artifacts cannot modify repository metadata",
197
+ );
198
+ }
199
+
200
+ function validateArtifactExclusions(
201
+ artifacts: InlineArtifact[],
202
+ generated: GeneratedFixture | null,
203
+ repository: RepositorySource | null,
204
+ ): void {
205
+ if (
206
+ !repository &&
207
+ !generated &&
208
+ artifacts.some((artifact) => artifact.gitExclude)
209
+ )
210
+ throw new EvaluationConfigurationError(
211
+ "Git-excluded preparation artifacts require a Git fixture",
212
+ );
213
+ }
214
+
215
+ function validateSemanticArtifactId(
216
+ artifacts: InlineArtifact[],
217
+ declarations: SemanticCheckDeclaration[],
218
+ ): void {
219
+ if (
220
+ declarations.length &&
221
+ artifacts.some((item) => item.id === "sevro.semantic.verdicts")
222
+ )
223
+ throw new EvaluationConfigurationError(
224
+ "preparation artifact uses a reserved semantic evidence ID",
225
+ );
226
+ }
227
+
228
+ async function prepareEvaluationArtifacts(
229
+ options: EvaluationOptions,
230
+ state: EnvironmentPlan,
231
+ checks: CheckPlan,
232
+ ) {
233
+ const fixtureSetup = preparedFixtureSetup(state.extensionPreparation);
234
+ validateSetupCapability(options, fixtureSetup);
235
+ validateSetupFixture(fixtureSetup, checks.generated, state.repository);
236
+ const requestedInstrumentation = requestedInstrumentationPlan(
237
+ options,
238
+ state.extensionPreparation,
239
+ );
240
+ const inlineArtifacts = await preparedEvaluationArtifacts(
241
+ options,
242
+ state,
243
+ checks.generated,
244
+ );
245
+ validateArtifactMetadata(inlineArtifacts, checks.generated, state.repository);
246
+ validateArtifactExclusions(
247
+ inlineArtifacts,
248
+ checks.generated,
249
+ state.repository,
250
+ );
251
+ validateSemanticArtifactId(inlineArtifacts, checks.semanticDeclarations);
252
+ return { fixtureSetup, requestedInstrumentation, inlineArtifacts };
253
+ }
254
+
255
+ type CheckPlan = ReturnType<typeof prepareEvaluationChecks>;
256
+ type ArtifactPlan = Awaited<ReturnType<typeof prepareEvaluationArtifacts>>;
257
+ type InvocationPlan = ReturnType<
258
+ typeof prepareInvocationPlan<{
259
+ extensionPreparation: EnvironmentPlan["extensionPreparation"];
260
+ inlineArtifacts: InlineArtifact[];
261
+ }>
262
+ >;
263
+ type EvaluationRouteRole = "candidate" | "semantic" | "advisory";
264
+ interface ActiveGrader {
265
+ id: string;
266
+ source: "builtin" | "extension";
267
+ version: string;
268
+ }
269
+
270
+ export interface PreparedFixture {
271
+ readonly projectRoot: string;
272
+ readonly repository: EnvironmentPlan["repository"];
273
+ readonly repositoryFixture: CheckPlan["repositoryFixture"];
274
+ readonly generated: CheckPlan["generated"];
275
+ readonly fixtureSetup: ArtifactPlan["fixtureSetup"];
276
+ readonly inlineArtifacts: InlineArtifact[];
277
+ }
278
+
279
+ export interface PreparedChecks extends Readonly<
280
+ Pick<
281
+ CheckPlan,
282
+ | "prepared"
283
+ | "preparedShell"
284
+ | "preparedGitHead"
285
+ | "preparedSemantic"
286
+ | "builtinDeclarations"
287
+ | "shellDeclarations"
288
+ | "gitHeadDeclarations"
289
+ | "semanticDeclarations"
290
+ | "extensionDeclarations"
291
+ | "replacedBuiltinGraders"
292
+ >
293
+ > {
294
+ readonly replaced: ReadonlySet<string>;
295
+ }
296
+
297
+ export type PreparedInvocation = Readonly<
298
+ Pick<
299
+ InvocationPlan,
300
+ | "codexMarketplace"
301
+ | "claudePluginDirs"
302
+ | "codexSkillInvocation"
303
+ | "codexRepositorySkillInvocation"
304
+ | "claudeSkillInvocation"
305
+ | "claudeRepositorySkillInvocation"
306
+ | "invocation"
307
+ | "invocationToken"
308
+ | "invocationPlaceholder"
309
+ | "legacyCodexPlaceholder"
310
+ >
311
+ >;
312
+
313
+ interface PreparedEvaluationInputs {
314
+ readonly fixture: PreparedFixture;
315
+ readonly checks: PreparedChecks;
316
+ readonly invocation: PreparedInvocation;
317
+ readonly jobs: number;
318
+ readonly requestedInstrumentation: InstrumentationRequest[];
319
+ readonly runner: EnvironmentPlan["runner"];
320
+ readonly project: EnvironmentPlan["project"];
321
+ }
322
+
323
+ export interface PreparedEvaluation extends PreparedEvaluationInputs {
324
+ readonly extensionData: Record<string, unknown>;
325
+ readonly route: ReturnType<typeof evaluationRoute>;
326
+ readonly routes: ReturnType<typeof evaluationRoutes>;
327
+ readonly redactedConfig: ReturnType<typeof redactedEvaluationConfiguration>;
328
+ readonly activeGraders: ActiveGrader[];
329
+ readonly evaluationIdentity: ReturnType<typeof createEvaluationIdentity>;
330
+ }
331
+
332
+ function extensionPreparationData(
333
+ options: EvaluationOptions,
334
+ preparation: EnvironmentPlan["extensionPreparation"],
335
+ ): Record<string, unknown> {
336
+ return options.extension
337
+ ? {
338
+ ...options.extension.resolvedCase.extensionData,
339
+ ...preparation?.extensionData,
340
+ }
341
+ : {};
342
+ }
343
+
344
+ function evaluationRoute(host: HostAdapter, role: EvaluationRouteRole) {
345
+ return { role, host: host.id, model: host.model, effort: host.effort };
346
+ }
347
+
348
+ function semanticEvaluationRoute(host: HostAdapter | undefined) {
349
+ if (!host)
350
+ throw new EvaluationConfigurationError(
351
+ "semantic checks require an explicit semantic host",
352
+ );
353
+ return evaluationRoute(host, "semantic");
354
+ }
355
+
356
+ function evaluationRoutes(
357
+ options: EvaluationOptions,
358
+ semantic: boolean,
359
+ candidate: ReturnType<typeof evaluationRoute>,
360
+ ) {
361
+ return [
362
+ candidate,
363
+ ...(semantic ? [semanticEvaluationRoute(options.semanticHost)] : []),
364
+ ...(options.advisoryHost
365
+ ? [evaluationRoute(options.advisoryHost, "advisory")]
366
+ : []),
367
+ ];
368
+ }
369
+
370
+ function redactedHostConfiguration(
371
+ options: EvaluationOptions,
372
+ semantic: boolean,
373
+ ) {
374
+ const selections = [
375
+ { role: "candidate", host: options.host },
376
+ { role: "semantic", host: semantic ? options.semanticHost : undefined },
377
+ { role: "advisory", host: options.advisoryHost },
378
+ ];
379
+ const configuration = Object.fromEntries(
380
+ selections.flatMap(({ role, host }) => {
381
+ if (host?.configuration === undefined) return [];
382
+ return [[role, host.configuration]];
383
+ }),
384
+ );
385
+ return Object.keys(configuration).length
386
+ ? { hostConfiguration: configuration }
387
+ : {};
388
+ }
389
+
390
+ function redactedExtensionConfiguration(options: EvaluationOptions) {
391
+ return {
392
+ extensionConfigurationDigest:
393
+ options.extension?.session.identity.configurationDigest ?? null,
394
+ ...(options.extension
395
+ ? {
396
+ extensionConfiguration:
397
+ options.extension.session.redactedConfiguration,
398
+ }
399
+ : {}),
400
+ };
401
+ }
402
+
403
+ function skillInvocationConfiguration(state: PreparedInvocation) {
404
+ return {
405
+ ...(state.codexSkillInvocation
406
+ ? { codexSkillInvocation: state.codexSkillInvocation }
407
+ : {}),
408
+ ...(state.codexRepositorySkillInvocation
409
+ ? { codexRepositorySkillInvocation: state.codexRepositorySkillInvocation }
410
+ : {}),
411
+ ...(state.claudeSkillInvocation
412
+ ? { claudeSkillInvocation: state.claudeSkillInvocation }
413
+ : {}),
414
+ ...(state.claudeRepositorySkillInvocation
415
+ ? {
416
+ claudeRepositorySkillInvocation:
417
+ state.claudeRepositorySkillInvocation,
418
+ }
419
+ : {}),
420
+ };
421
+ }
422
+
423
+ function redactedPreparationConfiguration(state: PreparedEvaluationInputs) {
424
+ return {
425
+ ...(state.fixture.fixtureSetup
426
+ ? { fixtureSetupDigest: hashJson(state.fixture.fixtureSetup) }
427
+ : {}),
428
+ ...(state.invocation.codexMarketplace
429
+ ? { codexMarketplace: state.invocation.codexMarketplace }
430
+ : {}),
431
+ ...(state.invocation.claudePluginDirs
432
+ ? { claudePluginDirs: state.invocation.claudePluginDirs }
433
+ : {}),
434
+ };
435
+ }
436
+
437
+ function redactedAdvisoryConfiguration(options: EvaluationOptions) {
438
+ return options.advisoryHost
439
+ ? {
440
+ advisoryExcludedPaths: [
441
+ ...(options.advisoryExcludedPaths ?? []),
442
+ ].sort(),
443
+ }
444
+ : {};
445
+ }
446
+
447
+ function redactedEvaluationConfiguration(
448
+ options: EvaluationOptions,
449
+ state: PreparedEvaluationInputs,
450
+ ) {
451
+ return {
452
+ condition: options.condition,
453
+ executionMode: options.dry ? "dry" : "executed",
454
+ ...(options.runtimePolicy ? { runtimePolicy: options.runtimePolicy } : {}),
455
+ trialCount: options.trialCount,
456
+ jobs: state.jobs,
457
+ passThreshold: options.passThreshold,
458
+ ...(options.assessmentSettings
459
+ ? { runSettings: options.assessmentSettings }
460
+ : {}),
461
+ ...redactedHostConfiguration(
462
+ options,
463
+ state.checks.preparedSemantic.length > 0,
464
+ ),
465
+ ...redactedExtensionConfiguration(options),
466
+ ...redactedPreparationConfiguration(state),
467
+ ...skillInvocationConfiguration(state.invocation),
468
+ ...redactedAdvisoryConfiguration(options),
469
+ };
470
+ }
471
+
472
+ function activeEvaluationGraders(
473
+ options: EvaluationOptions,
474
+ state: PreparedChecks,
475
+ ): ActiveGrader[] {
476
+ const graders: ActiveGrader[] = [
477
+ ...new Set(
478
+ [
479
+ ...state.builtinDeclarations,
480
+ ...state.shellDeclarations,
481
+ ...state.gitHeadDeclarations,
482
+ ...state.semanticDeclarations,
483
+ ].map((check) => check.grader),
484
+ ),
485
+ ]
486
+ .sort()
487
+ .map((id) => ({ id, source: "builtin", version: "1.0.0" }));
488
+ if (options.extension) {
489
+ for (const id of [
490
+ ...new Set(state.extensionDeclarations.map((check) => check.grader)),
491
+ ].sort())
492
+ graders.push({
493
+ id,
494
+ source: "extension",
495
+ version: options.extension.session.identity.version,
496
+ });
497
+ }
498
+ return graders;
499
+ }
500
+
501
+ function extensionIdentityDimensions(options: EvaluationOptions) {
502
+ return {
503
+ extensionDigest: options.extension?.session.identity.sourceDigest ?? null,
504
+ extensionProtocol: options.extension?.session.identity.protocol ?? null,
505
+ };
506
+ }
507
+
508
+ function caseIdentityData(
509
+ caseData: ResolvedCase,
510
+ extensionData: Record<string, unknown>,
511
+ ) {
512
+ return {
513
+ id: caseData.id,
514
+ prompt: caseData.prompt,
515
+ ...(caseData.followUpPrompt !== undefined
516
+ ? { followUpPrompt: caseData.followUpPrompt }
517
+ : {}),
518
+ extensionData,
519
+ };
520
+ }
521
+
522
+ function baseFixtureIdentity(
523
+ state: PreparedFixture,
524
+ fixture: ResolvedCase["fixture"],
525
+ ) {
526
+ if (state.generated) return state.generated;
527
+ if (state.repository)
528
+ return { ...state.repositoryFixture, revision: state.repository.revision };
529
+ return { files: fixture.files };
530
+ }
531
+
532
+ function artifactIdentityData(artifacts: InlineArtifact[]) {
533
+ return artifacts.map(
534
+ ({ id, relativePath, sha256, gitExclude, executable }) => ({
535
+ id,
536
+ relativePath,
537
+ sha256,
538
+ ...(gitExclude ? { gitExclude: true } : {}),
539
+ ...(executable ? { executable: true } : {}),
540
+ }),
541
+ );
542
+ }
543
+
544
+ function fixtureIdentityData(
545
+ state: PreparedEvaluationInputs,
546
+ fixture: ResolvedCase["fixture"],
547
+ ) {
548
+ return {
549
+ ...baseFixtureIdentity(state.fixture, fixture),
550
+ artifacts: artifactIdentityData(state.fixture.inlineArtifacts),
551
+ ...(state.fixture.fixtureSetup
552
+ ? { fixtureSetup: state.fixture.fixtureSetup }
553
+ : {}),
554
+ ...(state.invocation.codexMarketplace
555
+ ? { codexMarketplace: state.invocation.codexMarketplace }
556
+ : {}),
557
+ ...(state.invocation.claudePluginDirs
558
+ ? { claudePluginDirs: state.invocation.claudePluginDirs }
559
+ : {}),
560
+ ...skillInvocationConfiguration(state.invocation),
561
+ };
562
+ }
563
+
564
+ function evaluatorIdentityData(
565
+ options: EvaluationOptions,
566
+ extensionData: Record<string, unknown>,
567
+ ) {
568
+ return {
569
+ policy: "sevro.builtin-output.v1",
570
+ extension: options.extension?.session.identity ?? null,
571
+ extensionData,
572
+ };
573
+ }
574
+
575
+ type IdentityMetadata = {
576
+ extensionData: Record<string, unknown>;
577
+ route: ReturnType<typeof evaluationRoute>;
578
+ routes: ReturnType<typeof evaluationRoutes>;
579
+ redactedConfig: ReturnType<typeof redactedEvaluationConfiguration>;
580
+ activeGraders: ActiveGrader[];
581
+ };
582
+
583
+ function evaluationComparisonIdentity(
584
+ options: EvaluationOptions,
585
+ prepared: PreparedEvaluationInputs,
586
+ context: IdentityMetadata,
587
+ ) {
588
+ try {
589
+ return createEvaluationIdentity({
590
+ runnerBuildDigest: options.runnerBuildDigest,
591
+ projectDigest: options.projectDigest,
592
+ configurationDigest: hashJson(context.redactedConfig),
593
+ ...extensionIdentityDimensions(options),
594
+ caseDigest: hashJson(
595
+ caseIdentityData(options.case, context.extensionData),
596
+ ),
597
+ fixtureDigest: hashJson(
598
+ fixtureIdentityData(prepared, options.case.fixture),
599
+ ),
600
+ checksDigest: hashJson(options.case.checks),
601
+ requiredEvidenceDigest: hashJson(options.case.requiredEvidence),
602
+ evaluatorDigest: hashJson(
603
+ evaluatorIdentityData(options, context.extensionData),
604
+ ),
605
+ graderDigest: hashJson(context.activeGraders),
606
+ instrumentationDigest: hashJson({
607
+ requested: prepared.requestedInstrumentation,
608
+ applied: prepared.requestedInstrumentation,
609
+ }),
610
+ routeDigest: hashJson(
611
+ context.routes.length > 1 ? context.routes : context.route,
612
+ ),
613
+ condition: options.condition,
614
+ trialCount: options.trialCount,
615
+ passThreshold: options.passThreshold,
616
+ });
617
+ } catch (cause) {
618
+ throw new EvaluationConfigurationError(
619
+ "invalid evaluation identity inputs",
620
+ { cause },
621
+ );
622
+ }
623
+ }
624
+
625
+ function prepareEvaluationIdentity(
626
+ options: EvaluationOptions,
627
+ state: PreparedEvaluationInputs,
628
+ preparation: EnvironmentPlan["extensionPreparation"],
629
+ ) {
630
+ const extensionData = extensionPreparationData(options, preparation);
631
+ const route = evaluationRoute(options.host, "candidate");
632
+ const routes = evaluationRoutes(
633
+ options,
634
+ state.checks.preparedSemantic.length > 0,
635
+ route,
636
+ );
637
+ const redactedConfig = snapshotEvaluationConfiguration(options, state);
638
+ const activeGraders = activeEvaluationGraders(options, state.checks);
639
+ const context = {
640
+ extensionData,
641
+ route,
642
+ routes,
643
+ redactedConfig,
644
+ activeGraders,
645
+ };
646
+ const evaluationIdentity = evaluationComparisonIdentity(
647
+ options,
648
+ state,
649
+ context,
650
+ );
651
+ return { ...state, ...context, evaluationIdentity };
652
+ }
653
+
654
+ function snapshotEvaluationConfiguration(
655
+ options: EvaluationOptions,
656
+ state: PreparedEvaluationInputs,
657
+ ): ReturnType<typeof redactedEvaluationConfiguration> {
658
+ try {
659
+ return JSON.parse(
660
+ canonicalJson(redactedEvaluationConfiguration(options, state)),
661
+ ) as ReturnType<typeof redactedEvaluationConfiguration>;
662
+ } catch (cause) {
663
+ throw new EvaluationConfigurationError(
664
+ "invalid evaluation identity inputs",
665
+ { cause },
666
+ );
667
+ }
668
+ }
669
+
670
+ function preparedChecks(checks: CheckPlan): PreparedChecks {
671
+ return {
672
+ prepared: checks.prepared,
673
+ preparedShell: checks.preparedShell,
674
+ preparedGitHead: checks.preparedGitHead,
675
+ preparedSemantic: checks.preparedSemantic,
676
+ builtinDeclarations: checks.builtinDeclarations,
677
+ shellDeclarations: checks.shellDeclarations,
678
+ gitHeadDeclarations: checks.gitHeadDeclarations,
679
+ semanticDeclarations: checks.semanticDeclarations,
680
+ extensionDeclarations: checks.extensionDeclarations,
681
+ replacedBuiltinGraders: checks.replacedBuiltinGraders,
682
+ replaced: checks.replaced,
683
+ };
684
+ }
685
+
686
+ function preparedInvocation(invocation: InvocationPlan): PreparedInvocation {
687
+ return {
688
+ codexMarketplace: invocation.codexMarketplace,
689
+ claudePluginDirs: invocation.claudePluginDirs,
690
+ codexSkillInvocation: invocation.codexSkillInvocation,
691
+ codexRepositorySkillInvocation: invocation.codexRepositorySkillInvocation,
692
+ claudeSkillInvocation: invocation.claudeSkillInvocation,
693
+ claudeRepositorySkillInvocation: invocation.claudeRepositorySkillInvocation,
694
+ invocation: invocation.invocation,
695
+ invocationToken: invocation.invocationToken,
696
+ invocationPlaceholder: invocation.invocationPlaceholder,
697
+ legacyCodexPlaceholder: invocation.legacyCodexPlaceholder,
698
+ };
699
+ }
700
+
701
+ export async function prepareEvaluation(
702
+ options: EvaluationOptions,
703
+ ): Promise<PreparedEvaluation> {
704
+ const checks = prepareEvaluationChecks(options);
705
+ const environment = await prepareEvaluationEnvironment(options);
706
+ const artifacts = await prepareEvaluationArtifacts(
707
+ options,
708
+ environment,
709
+ checks,
710
+ );
711
+ const invocation = prepareInvocationPlan(options, {
712
+ extensionPreparation: environment.extensionPreparation,
713
+ inlineArtifacts: artifacts.inlineArtifacts,
714
+ });
715
+ const prepared: PreparedEvaluationInputs = {
716
+ fixture: {
717
+ projectRoot: environment.projectRoot,
718
+ repository: environment.repository,
719
+ repositoryFixture: checks.repositoryFixture,
720
+ generated: checks.generated,
721
+ fixtureSetup: artifacts.fixtureSetup,
722
+ inlineArtifacts: artifacts.inlineArtifacts,
723
+ },
724
+ checks: preparedChecks(checks),
725
+ invocation: preparedInvocation(invocation),
726
+ jobs: checks.jobs,
727
+ requestedInstrumentation: artifacts.requestedInstrumentation,
728
+ runner: environment.runner,
729
+ project: environment.project,
730
+ };
731
+ return prepareEvaluationIdentity(
732
+ options,
733
+ prepared,
734
+ environment.extensionPreparation,
735
+ );
736
+ }