@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.
- package/CONTRIBUTING.md +22 -6
- package/README.md +5 -1
- package/docs/README.md +19 -15
- package/docs/architecture.md +138 -17
- package/docs/assets/README.md +5 -4
- package/docs/builtin-graders-v1.md +17 -3
- package/docs/codex-host.md +155 -49
- package/docs/contracts.md +2 -1
- package/docs/creating-cases.md +9 -2
- package/docs/development-cli.md +187 -19
- package/docs/documentation-quality.md +12 -2
- package/docs/evaluation-workflow.md +110 -0
- package/docs/extension-protocol-v1.md +26 -3
- package/docs/getting-started.md +23 -2
- package/docs/guide-evaluation.md +33 -1
- package/docs/identity-v1.md +25 -2
- package/docs/installing.md +11 -11
- package/docs/native-controls.md +76 -5
- package/docs/native-hosts.md +196 -7
- package/docs/reading-results.md +102 -0
- package/docs/recipes.md +489 -0
- package/docs/releases.md +31 -12
- package/docs/report-v1.md +29 -1
- package/docs/results-v1.md +141 -4
- package/docs/run-settings-v1.md +147 -0
- package/docs/runtime-v1.md +307 -18
- package/docs/troubleshooting.md +12 -12
- package/docs/typescript-quality.md +89 -26
- package/examples/recipes/candidate.ts +26 -0
- package/examples/recipes/cases/coding-change.json +21 -0
- package/examples/recipes/cases/host-tools.json +21 -0
- package/examples/recipes/cases/mise.json +21 -0
- package/examples/recipes/cases/node.json +21 -0
- package/examples/recipes/cases/python-uv.json +21 -0
- package/examples/recipes/cases/semantic.json +17 -0
- package/examples/recipes/coding-change/check.ts +23 -0
- package/examples/recipes/extension-artifacts.ts +65 -0
- package/examples/recipes/extension-methods.ts +174 -0
- package/examples/recipes/extension.ts +37 -0
- package/examples/recipes/repository-skill/.agents/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/.claude/skills/startup-guide/SKILL.md +7 -0
- package/examples/recipes/repository-skill/README.md +3 -0
- package/examples/recipes/run-inputs.ts +188 -0
- package/examples/recipes/run-options.ts +131 -0
- package/examples/recipes/run.ts +63 -0
- package/examples/recipes/runtime/empty.json +1 -0
- package/examples/recipes/runtime/goals.json +1 -0
- package/examples/recipes/runtime/mise.json +6 -0
- package/examples/recipes/runtime/path.json +1 -0
- package/examples/recipes/runtime/plugins.json +4 -0
- package/examples/recipes/runtime/python-uv.json +10 -0
- package/examples/recipes/semantic-judge.ts +29 -0
- package/examples/templates/coding/adapter.ts +17 -0
- package/examples/templates/coding/case.json +15 -0
- package/examples/templates/repository-skill/.agents/skills/startup-guide/SKILL.md +6 -0
- package/examples/templates/repository-skill/extension-data.ts +64 -0
- package/examples/templates/repository-skill/extension.ts +31 -0
- package/examples/templates/response/adapter.ts +12 -0
- package/examples/templates/response/case.json +13 -0
- package/package.json +11 -5
- package/schemas/report-v1.schema.json +9 -0
- package/schemas/run-evidence-v1.schema.json +2 -1
- package/schemas/runtime-v1.schema.json +63 -7
- package/src/advisory.ts +2 -2
- package/src/candidate-artifacts.ts +206 -0
- package/src/candidate-codex-state.ts +185 -0
- package/src/case-validation.ts +73 -50
- package/src/cli/cancellation.ts +20 -0
- package/src/cli/command.ts +87 -0
- package/src/cli/completion.ts +15 -0
- package/src/cli/demo.ts +60 -0
- package/src/cli/evaluation.ts +55 -0
- package/src/cli/extension.ts +79 -0
- package/src/cli/help.ts +198 -0
- package/src/cli/hosts.ts +55 -0
- package/src/cli/init-settings.ts +30 -0
- package/src/cli/init.ts +111 -0
- package/src/{cli-invocation.ts → cli/invocation.ts} +214 -218
- package/src/cli/loading.ts +151 -0
- package/src/cli/preparation.ts +154 -0
- package/src/cli/presentation.ts +93 -0
- package/src/cli/progress.ts +6 -0
- package/src/cli/run-manifest.ts +128 -0
- package/src/cli/run-options.ts +403 -0
- package/src/cli/run-overrides.ts +69 -0
- package/src/cli/settings.ts +154 -0
- package/src/cli/summary.ts +43 -0
- package/src/cli.ts +13 -591
- package/src/contract-types.ts +94 -0
- package/src/engine.ts +13 -5
- package/src/evaluation/advisory.ts +150 -0
- package/src/{evaluation-check-plan.ts → evaluation/check-plan.ts} +103 -67
- package/src/evaluation/collection.ts +88 -0
- package/src/evaluation/context.ts +10 -0
- package/src/{evaluation-grading-host.ts → evaluation/grading-host.ts} +5 -5
- package/src/evaluation/grading.ts +235 -0
- package/src/{evaluation-invocation-plan.ts → evaluation/invocation-plan.ts} +147 -58
- package/src/{evaluation-plan.ts → evaluation/plan.ts} +208 -141
- package/src/evaluation/progress.ts +19 -0
- package/src/evaluation/resources.ts +241 -0
- package/src/evaluation/run.ts +206 -0
- package/src/{evaluation-semantic.ts → evaluation/semantic.ts} +133 -115
- package/src/evaluation/trial.ts +800 -0
- package/src/evaluation/types.ts +207 -0
- package/src/evidence/host-results.ts +85 -0
- package/src/evidence/retained-artifacts.ts +18 -0
- package/src/evidence/trial-writer.ts +203 -0
- package/src/evidence/usage.ts +17 -0
- package/src/extension-client.ts +34 -96
- package/src/extension-session.ts +24 -48
- package/src/{advisory-fixture.ts → fixtures/advisory.ts} +15 -76
- package/src/{fixture-bin.ts → fixtures/bin.ts} +1 -1
- package/src/{fixture-cleanup.ts → fixtures/cleanup.ts} +13 -7
- package/src/fixtures/create.ts +183 -0
- package/src/{generated-fixture.ts → fixtures/generated.ts} +10 -53
- package/src/{git-hooks.ts → fixtures/git-hooks.ts} +1 -1
- package/src/{preparation.ts → fixtures/preparation.ts} +6 -16
- package/src/{repository-fixture.ts → fixtures/repository.ts} +30 -64
- package/src/{fixture-setup.ts → fixtures/setup.ts} +33 -51
- package/src/{fixture-tools.ts → fixtures/tools.ts} +1 -1
- package/src/fixtures/workspace-state.ts +212 -0
- package/src/generated/cli-result.d.cts +2 -1
- package/src/generated/extension.d.cts +2 -1
- package/src/generated/report.cjs +1 -1
- package/src/generated/report.d.cts +2 -1
- package/src/generated/run-evidence.cjs +1 -1
- package/src/generated/run-evidence.d.cts +2 -1
- package/src/generated/runtime.cjs +1 -1
- package/src/generated/runtime.d.cts +2 -1
- package/src/git.ts +93 -0
- package/src/graders/git-head.ts +29 -84
- package/src/graders/shell.ts +57 -68
- package/src/host-diagnostic-redaction.ts +52 -0
- package/src/host-diagnostics.ts +331 -0
- package/src/hosts/claude-continuation.ts +57 -40
- package/src/hosts/claude-credential.ts +160 -63
- package/src/hosts/claude-events.ts +34 -18
- package/src/hosts/claude-keychain.ts +78 -21
- package/src/hosts/claude-login-recovery.ts +85 -0
- package/src/hosts/claude-login-refresh.ts +178 -0
- package/src/hosts/claude-login-store.ts +162 -0
- package/src/hosts/claude-login.ts +107 -0
- package/src/hosts/claude-native-goal.ts +15 -12
- package/src/hosts/claude-process.ts +10 -50
- package/src/hosts/claude-refresh-lock.ts +81 -0
- package/src/hosts/claude-repository-invocation.ts +9 -34
- package/src/hosts/claude-settings.ts +91 -14
- package/src/hosts/claude-skill-activations.ts +169 -0
- package/src/hosts/claude-state.ts +46 -16
- package/src/hosts/claude-tool-calls.ts +13 -10
- package/src/hosts/claude.ts +122 -73
- package/src/hosts/codex-app-server.ts +78 -52
- package/src/hosts/codex-configuration.ts +5 -10
- package/src/hosts/codex-exec-controls.ts +516 -0
- package/src/hosts/codex-marketplace.ts +12 -10
- package/src/hosts/codex-native-calls.ts +15 -122
- package/src/hosts/codex-process.ts +10 -54
- package/src/hosts/codex-profile.ts +50 -21
- package/src/hosts/codex-session-files.ts +1 -9
- package/src/hosts/codex-state.ts +141 -19
- package/src/hosts/codex.ts +221 -179
- package/src/hosts/isolation-common.ts +15 -11
- package/src/hosts/isolation-roots.ts +13 -20
- package/src/hosts/linux-sandbox.ts +27 -3
- package/src/hosts/mac-sandbox.ts +111 -18
- package/src/hosts/native-controls.ts +72 -22
- package/src/hosts/native-request.ts +55 -0
- package/src/hosts/process-lifecycle.ts +221 -45
- package/src/hosts/protected-worktrees.ts +1 -1
- package/src/hosts/skill-hook-catalog.ts +131 -0
- package/src/hosts/skill-hook-handler.ts +374 -0
- package/src/hosts/skill-hooks.ts +577 -0
- package/src/instrumentation.ts +7 -8
- package/src/native-transcript-state.ts +22 -3
- package/src/native-transcript-view.ts +52 -13
- package/src/native-transcripts.ts +34 -15
- package/src/paths.ts +22 -0
- package/src/process-lifecycle.ts +150 -0
- package/src/provenance.ts +27 -7
- package/src/prune.ts +300 -0
- package/src/report.ts +63 -36
- package/src/resolved-case.ts +1 -1
- package/src/results-lock.ts +42 -0
- package/src/results.ts +14 -18
- package/src/run-owner.ts +4 -4
- package/src/{runtime-config.ts → runtime/config.ts} +182 -44
- package/src/{runtime-discovery-apple.ts → runtime/discovery-apple.ts} +3 -3
- package/src/{runtime-discovery-providers.ts → runtime/discovery-providers.ts} +3 -9
- package/src/{runtime-discovery.ts → runtime/discovery.ts} +4 -4
- package/src/{runtime-hook-processes.ts → runtime/hook-processes.ts} +1 -1
- package/src/{runtime-hooks.ts → runtime/hooks.ts} +33 -22
- package/src/{runtime-paths.ts → runtime/paths.ts} +6 -18
- package/src/{runtime-query.ts → runtime/query.ts} +5 -5
- package/src/{runtime-seeds.ts → runtime/seeds.ts} +3 -3
- package/src/runtime/shared-cache.ts +130 -0
- package/src/{runtime-state.ts → runtime/state.ts} +28 -11
- package/src/schema-types.ts +73 -161
- package/src/shell.ts +4 -0
- package/src/transcript-retention.ts +146 -0
- package/src/value-guards.ts +22 -0
- package/docs/assets/sevro-logo.png +0 -0
- package/docs/assets/sevro-symbol.svg +0 -1
- package/docs/assets/sevro-wordmark.svg +0 -1
- package/docs/cache-ownership-validation.md +0 -49
- package/docs/claude-continuation-validation.md +0 -47
- package/docs/concurrency-validation.md +0 -86
- package/docs/configuration-root-validation.md +0 -70
- package/docs/engine-slice.md +0 -114
- package/docs/evidence/runtime-startup-30/README.md +0 -185
- package/docs/evidence/runtime-startup-30/after.json +0 -319
- package/docs/evidence/runtime-startup-30/baseline-timeout.jsonl +0 -2
- package/docs/evidence/runtime-startup-30/before.json +0 -403
- package/docs/evidence/runtime-startup-30/consumer-smoke.json +0 -63
- package/docs/evidence/runtime-startup-30/full-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/minimal-policy-diagnostic.jsonl +0 -3
- package/docs/evidence/runtime-startup-30/plugin-template-assessment.json +0 -20
- package/docs/evidence/runtime-startup-30/seedless-timeout.jsonl +0 -2
- package/docs/evidence.md +0 -17
- package/docs/fixture-cleanup-validation.md +0 -70
- package/docs/guide-verification.json +0 -239
- package/src/evaluation-context.ts +0 -257
- package/src/evaluation-fixture.ts +0 -313
- package/src/evaluation-run.ts +0 -198
- package/src/evaluation-trial.ts +0 -946
- package/src/evaluation-types.ts +0 -149
- package/src/hosts/claude-nested-skills.ts +0 -272
- package/src/hosts/codex-command-output.ts +0 -510
- package/src/hosts/codex-executor-command.ts +0 -291
- package/src/hosts/codex-skill-reads.ts +0 -609
- /package/src/{trial-scheduler.ts → evaluation/scheduler.ts} +0 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { access, lstat, realpath, symlink } from "node:fs/promises";
|
|
2
|
+
import { constants } from "node:fs";
|
|
3
|
+
import { userInfo } from "node:os";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
|
+
import type { RuntimePolicy, RuntimeRole } from "./config";
|
|
6
|
+
import { declaredRuntimeRoots, requireRuntimeReadRoots } from "./paths";
|
|
7
|
+
|
|
8
|
+
export interface SharedCacheDirectory {
|
|
9
|
+
source: string;
|
|
10
|
+
target: string;
|
|
11
|
+
roles: RuntimeRole[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function resolveSharedCaches(
|
|
15
|
+
caches: SharedCacheDirectory[],
|
|
16
|
+
environment: Record<string, string>,
|
|
17
|
+
base: string,
|
|
18
|
+
protectedRoots: string[],
|
|
19
|
+
): Promise<SharedCacheDirectory[]> {
|
|
20
|
+
return Promise.all(
|
|
21
|
+
caches.map(async (cache) => {
|
|
22
|
+
const roots = await declaredRuntimeRoots(
|
|
23
|
+
[cache.source],
|
|
24
|
+
environment,
|
|
25
|
+
base,
|
|
26
|
+
);
|
|
27
|
+
requireRuntimeReadRoots(roots, protectedRoots);
|
|
28
|
+
const [declared] = roots;
|
|
29
|
+
if (!declared) throw new Error("runtime shared cache source is missing");
|
|
30
|
+
const source = await realpath(declared);
|
|
31
|
+
await requireSharedCache(source);
|
|
32
|
+
return { ...cache, source };
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function selectedSharedCaches(
|
|
38
|
+
policy: RuntimePolicy | undefined,
|
|
39
|
+
role: RuntimeRole = "candidate",
|
|
40
|
+
): SharedCacheDirectory[] {
|
|
41
|
+
return (policy?.sharedCacheDirectories ?? []).filter((cache) =>
|
|
42
|
+
cache.roles.includes(role),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function sharedCacheRoots(
|
|
47
|
+
policy: RuntimePolicy | undefined,
|
|
48
|
+
role: RuntimeRole = "candidate",
|
|
49
|
+
protectedRoots: string[],
|
|
50
|
+
): Promise<string[]> {
|
|
51
|
+
const roots = selectedSharedCaches(policy, role).map((cache) => cache.source);
|
|
52
|
+
return requireWritableCacheRoots(roots, protectedRoots);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function requireSharedCachePolicy(
|
|
56
|
+
policy: RuntimePolicy | undefined,
|
|
57
|
+
): void {
|
|
58
|
+
if (!policy) return;
|
|
59
|
+
const caches = policy.sharedCacheDirectories ?? [];
|
|
60
|
+
const protectedRoots = [
|
|
61
|
+
...policy.readOnlyRoots,
|
|
62
|
+
...(policy.seeds ?? []).map((seed) => seed.source),
|
|
63
|
+
];
|
|
64
|
+
for (const cache of caches) {
|
|
65
|
+
requireRuntimeReadRoots(
|
|
66
|
+
[cache.source],
|
|
67
|
+
[
|
|
68
|
+
...protectedRoots,
|
|
69
|
+
...caches
|
|
70
|
+
.filter((other) => other !== cache)
|
|
71
|
+
.map((other) => other.source),
|
|
72
|
+
],
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function sharedCacheDenyRoots(
|
|
78
|
+
policy: RuntimePolicy | undefined,
|
|
79
|
+
role: RuntimeRole = "candidate",
|
|
80
|
+
): string[] {
|
|
81
|
+
return (policy?.sharedCacheDirectories ?? [])
|
|
82
|
+
.filter((cache) => !cache.roles.includes(role))
|
|
83
|
+
.map((cache) => cache.source);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function requireWritableCacheRoots(
|
|
87
|
+
roots: string[],
|
|
88
|
+
protectedRoots: string[],
|
|
89
|
+
): Promise<string[]> {
|
|
90
|
+
requireRuntimeReadRoots(roots, protectedRoots);
|
|
91
|
+
for (const root of roots) await requireSharedCache(root);
|
|
92
|
+
return roots;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
async function requireSharedCache(source: string): Promise<void> {
|
|
96
|
+
if (!isAbsolute(source) || (await realpath(source)) !== source)
|
|
97
|
+
throw new Error("runtime shared cache must be a canonical directory");
|
|
98
|
+
const info = await lstat(source);
|
|
99
|
+
if (!info.isDirectory() || info.uid !== userInfo().uid)
|
|
100
|
+
throw new Error("runtime shared cache must be an owned directory");
|
|
101
|
+
await access(source, constants.R_OK | constants.W_OK | constants.X_OK);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** A slot aliases the operator-selected cache; its contents are never copied. */
|
|
105
|
+
export async function linkSharedCaches(
|
|
106
|
+
policy: RuntimePolicy,
|
|
107
|
+
role: RuntimeRole,
|
|
108
|
+
root: string,
|
|
109
|
+
): Promise<void> {
|
|
110
|
+
for (const cache of selectedSharedCaches(policy, role)) {
|
|
111
|
+
const target = join(root, cache.target);
|
|
112
|
+
try {
|
|
113
|
+
await symlink(cache.source, target, "dir");
|
|
114
|
+
} catch (cause) {
|
|
115
|
+
if ((cause as NodeJS.ErrnoException).code !== "EEXIST") throw cause;
|
|
116
|
+
await requireCacheSlot(target, cache.source, cause);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function requireCacheSlot(
|
|
122
|
+
target: string,
|
|
123
|
+
source: string,
|
|
124
|
+
cause: unknown,
|
|
125
|
+
): Promise<void> {
|
|
126
|
+
if (!(await lstat(target)).isSymbolicLink())
|
|
127
|
+
throw new Error("runtime shared cache slot was replaced", { cause });
|
|
128
|
+
if ((await realpath(target)) !== source)
|
|
129
|
+
throw new Error("runtime shared cache slot changed its source", { cause });
|
|
130
|
+
}
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import { mkdir, realpath, stat } from "node:fs/promises";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import {
|
|
4
|
-
|
|
4
|
+
requireRuntimePolicy,
|
|
5
5
|
runtimeRoleSeeds,
|
|
6
6
|
type RuntimeRole,
|
|
7
|
-
requireRuntimeSeedTargets,
|
|
8
7
|
type RuntimePolicy,
|
|
9
|
-
} from "./
|
|
10
|
-
import { materializeRuntimeSeed } from "./
|
|
11
|
-
import { requireRuntimeReadRoots } from "./
|
|
12
|
-
import { hashJson } from "
|
|
8
|
+
} from "./config";
|
|
9
|
+
import { materializeRuntimeSeed } from "./seeds";
|
|
10
|
+
import { requireRuntimeReadRoots } from "./paths";
|
|
11
|
+
import { hashJson } from "../identity";
|
|
12
|
+
import {
|
|
13
|
+
linkSharedCaches,
|
|
14
|
+
sharedCacheRoots,
|
|
15
|
+
selectedSharedCaches,
|
|
16
|
+
} from "./shared-cache";
|
|
13
17
|
|
|
14
|
-
export type { RuntimeRole } from "./
|
|
18
|
+
export type { RuntimeRole } from "./config";
|
|
15
19
|
|
|
16
20
|
export function runtimeObservations(
|
|
17
21
|
policy: RuntimePolicy | undefined,
|
|
@@ -29,6 +33,9 @@ export function runtimeObservations(
|
|
|
29
33
|
environmentNames: Object.keys(policy.environment).sort(),
|
|
30
34
|
readOnlyRoots: policy.readOnlyRoots,
|
|
31
35
|
...discoveryObservations(policy),
|
|
36
|
+
...(policy.sharedCacheDirectories?.length
|
|
37
|
+
? { sharedCacheDirectories: selectedSharedCaches(policy, role) }
|
|
38
|
+
: {}),
|
|
32
39
|
seeds: runtimeRoleSeeds(policy, role).map((seed) => ({
|
|
33
40
|
target: seed.target,
|
|
34
41
|
sha256: seed.sha256,
|
|
@@ -57,16 +64,19 @@ function runtimeDigest(policy: RuntimePolicy): string {
|
|
|
57
64
|
});
|
|
58
65
|
}
|
|
59
66
|
|
|
60
|
-
/**
|
|
67
|
+
/** Private role state contains copied seeds and aliases to explicitly shared caches. */
|
|
61
68
|
export async function prepareRuntimeState(
|
|
62
69
|
workspace: string,
|
|
63
70
|
policy: RuntimePolicy,
|
|
64
71
|
role: RuntimeRole,
|
|
65
72
|
privateRoot?: string,
|
|
66
73
|
) {
|
|
67
|
-
|
|
68
|
-
requireRuntimeSeedTargets(policy.seeds ?? []);
|
|
74
|
+
requireRuntimePolicy(policy);
|
|
69
75
|
const root = await runtimeRoot(workspace, role, privateRoot);
|
|
76
|
+
const writableCacheRoots = await sharedCacheRoots(policy, role, [
|
|
77
|
+
workspace,
|
|
78
|
+
root,
|
|
79
|
+
]);
|
|
70
80
|
const home = join(root, "home"),
|
|
71
81
|
temp = join(root, "tmp");
|
|
72
82
|
await Promise.all(
|
|
@@ -74,6 +84,7 @@ export async function prepareRuntimeState(
|
|
|
74
84
|
);
|
|
75
85
|
for (const seed of runtimeRoleSeeds(policy, role))
|
|
76
86
|
await copyRuntimeSeed(seed, join(root, seed.target));
|
|
87
|
+
await linkSharedCaches(policy, role, root);
|
|
77
88
|
const environment = Object.fromEntries(
|
|
78
89
|
Object.entries(policy.environment).map(([name, value]) => [
|
|
79
90
|
name,
|
|
@@ -87,7 +98,13 @@ export async function prepareRuntimeState(
|
|
|
87
98
|
UV_OFFLINE: "1",
|
|
88
99
|
PYTHONDONTWRITEBYTECODE: "1",
|
|
89
100
|
};
|
|
90
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
root,
|
|
103
|
+
home,
|
|
104
|
+
temp,
|
|
105
|
+
environment: isolatedEnvironment,
|
|
106
|
+
writableCacheRoots,
|
|
107
|
+
};
|
|
91
108
|
}
|
|
92
109
|
|
|
93
110
|
async function runtimeRoot(
|
package/src/schema-types.ts
CHANGED
|
@@ -1,83 +1,65 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import type { AdvisoryAssessment } from "./advisory";
|
|
2
|
+
import type {
|
|
3
|
+
ArtifactReference,
|
|
4
|
+
Assessment,
|
|
5
|
+
CheckOutcome,
|
|
6
|
+
Diagnostic,
|
|
7
|
+
DomainOutcome,
|
|
8
|
+
EvidenceRoute,
|
|
9
|
+
ExecutionStatus,
|
|
10
|
+
ExitCode,
|
|
11
|
+
GradingStatus,
|
|
12
|
+
MetricEvidence,
|
|
13
|
+
ObservationCompleteness,
|
|
14
|
+
ObservationEvidence,
|
|
15
|
+
OutcomeStatus,
|
|
16
|
+
RawResultReference,
|
|
17
|
+
TaskVerdict,
|
|
18
|
+
UsageEvidence,
|
|
19
|
+
} from "./contract-types";
|
|
20
|
+
import type { IdentityDimensions } from "./identity";
|
|
21
|
+
import type { InstrumentationRequest } from "./instrumentation";
|
|
22
|
+
|
|
23
|
+
/** Wire assessments additionally allow optional serialized error codes. */
|
|
24
|
+
export type WireAssessment = {
|
|
25
|
+
execution: Assessment["execution"] & { errorCode?: string };
|
|
26
|
+
grading: Assessment["grading"] & { errorCode?: string };
|
|
27
|
+
task: Assessment["task"];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Validated v1 wire data also represents failures before run allocation. */
|
|
31
|
+
export type CliResultData = WireAssessment & {
|
|
3
32
|
format: "sevro.cli-result.v1";
|
|
4
33
|
runId: string | null;
|
|
5
|
-
|
|
6
|
-
grading: CliResultDataGrading;
|
|
7
|
-
task: CliResultDataTask;
|
|
8
|
-
exitCode: 0 | 1 | 2 | 3 | 4 | 64 | 70 | 130 | 143;
|
|
34
|
+
exitCode: ExitCode;
|
|
9
35
|
evidencePath: string | null;
|
|
10
36
|
cases: Array<CliResultDataCase>;
|
|
11
|
-
diagnostic?:
|
|
12
|
-
};
|
|
13
|
-
type CliResultDataExecution = {
|
|
14
|
-
status: "completed" | "failed" | "cancelled" | "not_run";
|
|
15
|
-
errorCode?: string;
|
|
16
|
-
};
|
|
17
|
-
type CliResultDataGrading = {
|
|
18
|
-
status: "completed" | "error" | "unavailable" | "not_requested";
|
|
19
|
-
errorCode?: string;
|
|
20
|
-
};
|
|
21
|
-
type CliResultDataTask = { verdict: "passed" | "failed" | "not_assessed" };
|
|
22
|
-
type CliResultDataCheck = {
|
|
23
|
-
id: string;
|
|
24
|
-
grader: string;
|
|
25
|
-
status: "passed" | "failed" | "unavailable";
|
|
26
|
-
detail?: string;
|
|
27
|
-
evidenceRefs?: Array<string>;
|
|
37
|
+
diagnostic?: Diagnostic;
|
|
28
38
|
};
|
|
29
|
-
type
|
|
30
|
-
id: string;
|
|
31
|
-
status: "passed" | "failed" | "unavailable";
|
|
32
|
-
detail?: string;
|
|
33
|
-
evidenceRefs: Array<string>;
|
|
34
|
-
data?: Record<string, unknown>;
|
|
35
|
-
};
|
|
36
|
-
type CliResultDataTrial = {
|
|
39
|
+
export type CliResultDataTrial = WireAssessment & {
|
|
37
40
|
trial: number;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
task: CliResultDataTask;
|
|
41
|
-
checks: Array<CliResultDataCheck>;
|
|
42
|
-
domainOutcomes?: Array<CliResultDataDomainOutcome>;
|
|
41
|
+
checks: Array<CheckOutcome>;
|
|
42
|
+
domainOutcomes?: Array<DomainOutcome>;
|
|
43
43
|
artifactPath: string | null;
|
|
44
44
|
};
|
|
45
|
-
type CliResultDataCase = {
|
|
45
|
+
export type CliResultDataCase = WireAssessment & {
|
|
46
46
|
caseId: string;
|
|
47
|
-
execution: CliResultDataExecution;
|
|
48
|
-
grading: CliResultDataGrading;
|
|
49
|
-
task: CliResultDataTask;
|
|
50
47
|
trials: Array<CliResultDataTrial>;
|
|
51
48
|
};
|
|
52
|
-
|
|
49
|
+
|
|
50
|
+
/** Structural contracts for data validated by the versioned JSON schemas. */
|
|
53
51
|
export type RunEvidenceData = {
|
|
54
52
|
format: "sevro.run-evidence.v1";
|
|
55
53
|
runId: string;
|
|
56
54
|
evaluationIdentity: {
|
|
57
55
|
algorithm: "sevro.identity.v1";
|
|
58
56
|
digest: RunEvidenceDataDigest;
|
|
59
|
-
dimensions:
|
|
60
|
-
runnerBuildDigest: RunEvidenceDataDigest;
|
|
61
|
-
projectDigest: RunEvidenceDataDigest;
|
|
62
|
-
configurationDigest: RunEvidenceDataDigest;
|
|
63
|
-
extensionDigest: RunEvidenceDataDigest | null;
|
|
64
|
-
extensionProtocol: "sevro.extension.v1" | null;
|
|
65
|
-
caseDigest: RunEvidenceDataDigest;
|
|
66
|
-
fixtureDigest: RunEvidenceDataDigest;
|
|
67
|
-
checksDigest: RunEvidenceDataDigest;
|
|
68
|
-
requiredEvidenceDigest: RunEvidenceDataDigest;
|
|
69
|
-
evaluatorDigest: RunEvidenceDataDigest;
|
|
70
|
-
graderDigest: RunEvidenceDataDigest;
|
|
71
|
-
instrumentationDigest: RunEvidenceDataDigest;
|
|
72
|
-
routeDigest: RunEvidenceDataDigest;
|
|
73
|
-
condition: "passive" | "enforced";
|
|
74
|
-
trialCount: number;
|
|
75
|
-
passThreshold: number;
|
|
76
|
-
};
|
|
57
|
+
dimensions: IdentityDimensions;
|
|
77
58
|
};
|
|
78
59
|
configuration: {
|
|
79
60
|
digest: RunEvidenceDataDigest;
|
|
80
61
|
redacted: Record<string, unknown>;
|
|
62
|
+
resolved?: Record<string, unknown>;
|
|
81
63
|
};
|
|
82
64
|
runner: RunEvidenceDataRunner;
|
|
83
65
|
project: {
|
|
@@ -89,17 +71,17 @@ export type RunEvidenceData = {
|
|
|
89
71
|
condition: {
|
|
90
72
|
requested: "passive" | "enforced";
|
|
91
73
|
actual: "passive" | "enforced" | "unknown";
|
|
92
|
-
requestedInstrumentation:
|
|
93
|
-
appliedInstrumentation:
|
|
74
|
+
requestedInstrumentation: InstrumentationRequest[];
|
|
75
|
+
appliedInstrumentation: InstrumentationRequest[];
|
|
94
76
|
};
|
|
95
77
|
graders: {
|
|
96
78
|
active: Array<RunEvidenceDataGrader>;
|
|
97
79
|
replacedDefaults: Array<string>;
|
|
98
80
|
};
|
|
99
|
-
routes: Array<
|
|
81
|
+
routes: Array<EvidenceRoute>;
|
|
100
82
|
result: CliResultData;
|
|
101
83
|
trials: Array<RunEvidenceDataTrialEvidence>;
|
|
102
|
-
diagnostic?:
|
|
84
|
+
diagnostic?: Diagnostic;
|
|
103
85
|
};
|
|
104
86
|
type RunEvidenceDataDigest = string;
|
|
105
87
|
type RunEvidenceDataFileUrl = string;
|
|
@@ -126,49 +108,18 @@ type RunEvidenceDataExtension = {
|
|
|
126
108
|
capabilities: Array<string>;
|
|
127
109
|
replacements: { graders: Array<string>; taskVerdictPolicy: string | null };
|
|
128
110
|
};
|
|
129
|
-
type RunEvidenceDataInstrumentationList = Array<{
|
|
130
|
-
id: string;
|
|
131
|
-
configuration: Record<string, unknown>;
|
|
132
|
-
}>;
|
|
133
111
|
type RunEvidenceDataGrader = {
|
|
134
112
|
id: string;
|
|
135
113
|
source: "builtin" | "extension";
|
|
136
114
|
version: string;
|
|
137
115
|
};
|
|
138
|
-
type
|
|
139
|
-
role: "candidate" | "semantic" | "advisory";
|
|
140
|
-
host: string;
|
|
141
|
-
model: string;
|
|
142
|
-
effort: string;
|
|
143
|
-
};
|
|
144
|
-
type RunEvidenceDataAdvisoryReview = null | {
|
|
116
|
+
export type AdvisoryReviewData = {
|
|
145
117
|
status: "completed" | "failed" | "not_run";
|
|
146
|
-
assessment:
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
dimensions: {
|
|
150
|
-
correctness: number;
|
|
151
|
-
maintainability: number;
|
|
152
|
-
testQuality: number;
|
|
153
|
-
scopeDiscipline: number;
|
|
154
|
-
};
|
|
155
|
-
strengths: Array<string>;
|
|
156
|
-
weaknesses: Array<string>;
|
|
157
|
-
summary: string;
|
|
158
|
-
};
|
|
159
|
-
usage: {
|
|
160
|
-
inputTokens: number | null;
|
|
161
|
-
outputTokens: number | null;
|
|
162
|
-
costUsd: number | null;
|
|
163
|
-
complete: boolean;
|
|
164
|
-
};
|
|
165
|
-
rawResult: {
|
|
166
|
-
source: string;
|
|
167
|
-
path: RunEvidenceDataFileUrl | null;
|
|
168
|
-
sha256: RunEvidenceDataDigest | null;
|
|
169
|
-
};
|
|
118
|
+
assessment: AdvisoryAssessment | null;
|
|
119
|
+
usage: UsageEvidence;
|
|
120
|
+
rawResult: RawResultReference;
|
|
170
121
|
};
|
|
171
|
-
type RunEvidenceDataTrialEvidence = {
|
|
122
|
+
export type RunEvidenceDataTrialEvidence = {
|
|
172
123
|
caseId: string;
|
|
173
124
|
trial: number;
|
|
174
125
|
executionMode: "executed" | "dry" | "unknown";
|
|
@@ -176,50 +127,22 @@ type RunEvidenceDataTrialEvidence = {
|
|
|
176
127
|
condition: {
|
|
177
128
|
requested: "passive" | "enforced";
|
|
178
129
|
actual: "passive" | "enforced" | "unknown";
|
|
179
|
-
appliedInstrumentation:
|
|
130
|
+
appliedInstrumentation: InstrumentationRequest[];
|
|
180
131
|
};
|
|
181
|
-
observationCompleteness:
|
|
182
|
-
observations: Array<
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
completeness: "complete" | "partial" | "unavailable";
|
|
186
|
-
data: Record<string, unknown>;
|
|
187
|
-
}>;
|
|
188
|
-
metrics: Array<RunEvidenceDataMetric>;
|
|
189
|
-
domainOutcomes?: Array<{
|
|
190
|
-
id: string;
|
|
191
|
-
status: "passed" | "failed" | "unavailable";
|
|
192
|
-
detail?: string;
|
|
193
|
-
evidenceRefs: Array<string>;
|
|
194
|
-
data?: Record<string, unknown>;
|
|
195
|
-
}>;
|
|
132
|
+
observationCompleteness: ObservationCompleteness;
|
|
133
|
+
observations: Array<ObservationEvidence>;
|
|
134
|
+
metrics: Array<MetricEvidence>;
|
|
135
|
+
domainOutcomes?: Array<DomainOutcome>;
|
|
196
136
|
taskVerdictPolicy: null | {
|
|
197
137
|
id: string;
|
|
198
|
-
recommendation:
|
|
199
|
-
};
|
|
200
|
-
advisoryReview?: RunEvidenceDataAdvisoryReview;
|
|
201
|
-
routes: Array<RunEvidenceDataRoute>;
|
|
202
|
-
usage: {
|
|
203
|
-
inputTokens: number | null;
|
|
204
|
-
outputTokens: number | null;
|
|
205
|
-
costUsd: number | null;
|
|
206
|
-
complete: boolean;
|
|
138
|
+
recommendation: TaskVerdict | null;
|
|
207
139
|
};
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
artifactRefs: Array<{
|
|
214
|
-
id: string;
|
|
215
|
-
path: RunEvidenceDataFileUrl;
|
|
216
|
-
sha256: RunEvidenceDataDigest;
|
|
217
|
-
gitExclude?: boolean;
|
|
218
|
-
executable?: boolean;
|
|
219
|
-
}>;
|
|
140
|
+
advisoryReview?: AdvisoryReviewData | null;
|
|
141
|
+
routes: Array<EvidenceRoute>;
|
|
142
|
+
usage: UsageEvidence;
|
|
143
|
+
rawResult: RawResultReference;
|
|
144
|
+
artifactRefs: Array<ArtifactReference>;
|
|
220
145
|
};
|
|
221
|
-
type RunEvidenceDataDiagnostic = { code: string; message: string };
|
|
222
|
-
type RunEvidenceDataMetric = { id: string; value: number | null; unit: string };
|
|
223
146
|
export type ReportData = {
|
|
224
147
|
format: "sevro.report.v1";
|
|
225
148
|
inputs: Array<ReportDataInput>;
|
|
@@ -233,38 +156,32 @@ export type ReportData = {
|
|
|
233
156
|
};
|
|
234
157
|
type ReportDataPath = string;
|
|
235
158
|
type ReportDataDigest = string;
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
| "failed"
|
|
239
|
-
| "cancelled"
|
|
240
|
-
| "not_run"
|
|
241
|
-
| "error"
|
|
242
|
-
| "unavailable"
|
|
243
|
-
| "not_requested";
|
|
244
|
-
type ReportDataVerdict = "passed" | "failed" | "not_assessed";
|
|
159
|
+
/** The report wire schema deliberately allows either state family in rows. */
|
|
160
|
+
type ReportDataState = ExecutionStatus | GradingStatus;
|
|
245
161
|
type ReportDataInput = {
|
|
246
162
|
resultFile: ReportDataPath;
|
|
247
|
-
execution:
|
|
248
|
-
grading:
|
|
249
|
-
task:
|
|
250
|
-
exitCode:
|
|
163
|
+
execution: ExecutionStatus;
|
|
164
|
+
grading: GradingStatus;
|
|
165
|
+
task: TaskVerdict;
|
|
166
|
+
exitCode: ExitCode;
|
|
251
167
|
caseCount: number;
|
|
252
168
|
};
|
|
253
|
-
type ReportDataRoute =
|
|
169
|
+
type ReportDataRoute = Omit<EvidenceRoute, "role">;
|
|
254
170
|
type ReportDataOutcome = {
|
|
255
171
|
trial: number;
|
|
256
172
|
id: string;
|
|
257
|
-
status:
|
|
173
|
+
status: OutcomeStatus;
|
|
258
174
|
};
|
|
259
175
|
type ReportDataRow = {
|
|
176
|
+
nativeTranscripts?: { retained: number; pruned: number };
|
|
260
177
|
resultFile: ReportDataPath;
|
|
261
178
|
evidencePath: string | null;
|
|
262
179
|
runId: string | null;
|
|
263
180
|
caseId: string;
|
|
264
181
|
execution: ReportDataState;
|
|
265
182
|
grading: ReportDataState;
|
|
266
|
-
task:
|
|
267
|
-
exitCode:
|
|
183
|
+
task: TaskVerdict;
|
|
184
|
+
exitCode: ExitCode;
|
|
268
185
|
trialCount: number;
|
|
269
186
|
taskPassRate: number | null;
|
|
270
187
|
candidateDurationMs: number | null;
|
|
@@ -276,11 +193,6 @@ type ReportDataRow = {
|
|
|
276
193
|
requestedCondition: "passive" | "enforced" | null;
|
|
277
194
|
actualCondition: "passive" | "enforced" | "unknown" | null;
|
|
278
195
|
candidateRoute: ReportDataRoute | null;
|
|
279
|
-
routes: Array<
|
|
280
|
-
role: "candidate" | "semantic" | "advisory";
|
|
281
|
-
host: string;
|
|
282
|
-
model: string;
|
|
283
|
-
effort: string;
|
|
284
|
-
}>;
|
|
196
|
+
routes: Array<EvidenceRoute>;
|
|
285
197
|
domainOutcomes: Array<ReportDataOutcome>;
|
|
286
198
|
};
|
package/src/shell.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import type { RunEvidenceData } from "./schema-types";
|
|
5
|
+
import { canonicalJson, sha256 } from "./identity";
|
|
6
|
+
import type { ArtifactReference } from "./contract-types";
|
|
7
|
+
import { retainedFile } from "./results-lock";
|
|
8
|
+
import { atomicWriteJson } from "./storage";
|
|
9
|
+
|
|
10
|
+
function isNativeTranscript(artifact: ArtifactReference) {
|
|
11
|
+
return /^(?:sevro\.(?:semantic(?:\.[a-f0-9]{64})?|advisory)\.)?sevro\.native-transcripts\.bundle$/.test(
|
|
12
|
+
artifact.id,
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function transcriptArtifacts(evidence: RunEvidenceData) {
|
|
17
|
+
return evidence.trials.flatMap((trial) =>
|
|
18
|
+
trial.artifactRefs.filter(isNativeTranscript).map((artifact) => ({
|
|
19
|
+
caseId: trial.caseId,
|
|
20
|
+
trial: trial.trial,
|
|
21
|
+
...artifact,
|
|
22
|
+
})),
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function pruningRecord(evidence: RunEvidenceData) {
|
|
27
|
+
return {
|
|
28
|
+
format: "sevro.pruned-native-transcripts.v1",
|
|
29
|
+
runId: evidence.runId,
|
|
30
|
+
artifacts: transcriptArtifacts(evidence),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function pruningPath(evidence: RunEvidenceData) {
|
|
35
|
+
if (evidence.result.evidencePath === null)
|
|
36
|
+
throw new Error(
|
|
37
|
+
"native transcript retention requires a retained evidence path",
|
|
38
|
+
);
|
|
39
|
+
return join(
|
|
40
|
+
dirname(evidence.result.evidencePath),
|
|
41
|
+
"native-transcripts-pruned.json",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function transcriptPath(
|
|
46
|
+
evidence: RunEvidenceData,
|
|
47
|
+
artifact: ArtifactReference,
|
|
48
|
+
) {
|
|
49
|
+
const path = fileURLToPath(artifact.path);
|
|
50
|
+
if (dirname(path) !== dirname(pruningPath(evidence)))
|
|
51
|
+
throw new Error(
|
|
52
|
+
"native transcript artifact is outside its retained result directory",
|
|
53
|
+
);
|
|
54
|
+
if (metadataPaths(evidence).includes(path))
|
|
55
|
+
throw new Error(
|
|
56
|
+
"native transcript artifact aliases retained result metadata",
|
|
57
|
+
);
|
|
58
|
+
if (otherArtifactPaths(evidence).includes(path))
|
|
59
|
+
throw new Error(
|
|
60
|
+
"native transcript artifact aliases other retained evidence",
|
|
61
|
+
);
|
|
62
|
+
return path;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function otherArtifactPaths(evidence: RunEvidenceData) {
|
|
66
|
+
return evidence.trials
|
|
67
|
+
.flatMap((trial) => [
|
|
68
|
+
trial.rawResult.path,
|
|
69
|
+
trial.advisoryReview?.rawResult.path,
|
|
70
|
+
...trial.artifactRefs
|
|
71
|
+
.filter((ref) => !isNativeTranscript(ref))
|
|
72
|
+
.map((ref) => ref.path),
|
|
73
|
+
])
|
|
74
|
+
.filter((path): path is string => path != null)
|
|
75
|
+
.map((path) => fileURLToPath(path));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function metadataPaths(evidence: RunEvidenceData) {
|
|
79
|
+
const receipt = pruningPath(evidence);
|
|
80
|
+
return [
|
|
81
|
+
receipt,
|
|
82
|
+
evidence.result.evidencePath,
|
|
83
|
+
join(dirname(receipt), "result.json"),
|
|
84
|
+
join(dirname(receipt), "latest.json"),
|
|
85
|
+
...evidence.result.cases.flatMap((selected) =>
|
|
86
|
+
selected.trials.map((trial) => trial.artifactPath),
|
|
87
|
+
),
|
|
88
|
+
];
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function transcriptPresent(
|
|
92
|
+
evidence: RunEvidenceData,
|
|
93
|
+
artifact: ArtifactReference,
|
|
94
|
+
) {
|
|
95
|
+
const path = transcriptPath(evidence, artifact);
|
|
96
|
+
if (!(await retainedFile(path))) return false;
|
|
97
|
+
if (sha256(await readFile(path)) !== artifact.sha256)
|
|
98
|
+
throw new Error("retained native transcript evidence hash differs");
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Keep capture evidence immutable; a separate receipt records intentional removal. */
|
|
103
|
+
export async function pruneNativeTranscripts(evidence: RunEvidenceData) {
|
|
104
|
+
const artifacts = transcriptArtifacts(evidence);
|
|
105
|
+
if (!artifacts.length) return 0;
|
|
106
|
+
await atomicWriteJson(pruningPath(evidence), pruningRecord(evidence));
|
|
107
|
+
let removed = 0;
|
|
108
|
+
for (const artifact of artifacts) {
|
|
109
|
+
const path = transcriptPath(evidence, artifact);
|
|
110
|
+
if (!(await retainedFile(path))) continue;
|
|
111
|
+
await rm(path);
|
|
112
|
+
removed++;
|
|
113
|
+
}
|
|
114
|
+
return removed;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Missing native bytes require an exact receipt bound to their original references. */
|
|
118
|
+
async function hasPruningRecord(evidence: RunEvidenceData) {
|
|
119
|
+
const path = pruningPath(evidence);
|
|
120
|
+
const marked = await retainedFile(path);
|
|
121
|
+
if (marked) {
|
|
122
|
+
const receipt: unknown = JSON.parse(await readFile(path, "utf8"));
|
|
123
|
+
if (canonicalJson(receipt) !== canonicalJson(pruningRecord(evidence)))
|
|
124
|
+
throw new Error(
|
|
125
|
+
"native transcript pruning record differs from retained evidence",
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
return marked !== null;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function nativeTranscriptRetention(
|
|
132
|
+
evidence: RunEvidenceData | null,
|
|
133
|
+
) {
|
|
134
|
+
if (!evidence) return [];
|
|
135
|
+
const marked = await hasPruningRecord(evidence);
|
|
136
|
+
const states = [];
|
|
137
|
+
for (const artifact of transcriptArtifacts(evidence)) {
|
|
138
|
+
const present = await transcriptPresent(evidence, artifact);
|
|
139
|
+
if (!present && !marked)
|
|
140
|
+
throw new Error(
|
|
141
|
+
"retained native transcript evidence is missing without a pruning record",
|
|
142
|
+
);
|
|
143
|
+
states.push({ caseId: artifact.caseId, pruned: !present });
|
|
144
|
+
}
|
|
145
|
+
return states;
|
|
146
|
+
}
|