@hawkeyexl/manni 4.2.0 → 4.3.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 (43) hide show
  1. package/README.md +2 -2
  2. package/dist/batch-7JJWXWSF.js +18 -0
  3. package/dist/batch-7JJWXWSF.js.map +1 -0
  4. package/dist/calibrate-IA2ARFMH.js +13 -0
  5. package/dist/calibrate-IA2ARFMH.js.map +1 -0
  6. package/dist/capture-C32JXIFN.js +15 -0
  7. package/dist/capture-C32JXIFN.js.map +1 -0
  8. package/dist/chunk-3R23C5DT.js +120 -0
  9. package/dist/chunk-3R23C5DT.js.map +1 -0
  10. package/dist/chunk-CI2GOU3A.js +4447 -0
  11. package/dist/chunk-CI2GOU3A.js.map +1 -0
  12. package/dist/{chunk-TH565O2N.js → chunk-DP2ZA3WH.js} +898 -278
  13. package/dist/chunk-DP2ZA3WH.js.map +1 -0
  14. package/dist/chunk-ESJ5W7EV.js +297 -0
  15. package/dist/chunk-ESJ5W7EV.js.map +1 -0
  16. package/dist/chunk-IOYXPBCL.js +37 -0
  17. package/dist/chunk-IOYXPBCL.js.map +1 -0
  18. package/dist/chunk-KTSJ2ZHA.js +40 -0
  19. package/dist/chunk-KTSJ2ZHA.js.map +1 -0
  20. package/dist/{chunk-BNHQWIAE.js → chunk-MFDD3YGU.js} +2 -2
  21. package/dist/chunk-MOQXWXSG.js +1941 -0
  22. package/dist/chunk-MOQXWXSG.js.map +1 -0
  23. package/dist/{chunk-UYDRDFYF.js → chunk-O6RVQKK6.js} +1943 -909
  24. package/dist/chunk-O6RVQKK6.js.map +1 -0
  25. package/dist/chunk-QROCFW5S.js +174 -0
  26. package/dist/chunk-QROCFW5S.js.map +1 -0
  27. package/dist/chunk-RVVCA4WL.js +379 -0
  28. package/dist/chunk-RVVCA4WL.js.map +1 -0
  29. package/dist/cli.js +422 -42
  30. package/dist/cli.js.map +1 -1
  31. package/dist/docmeta.js +3 -2
  32. package/dist/docmeta.js.map +1 -1
  33. package/dist/index.d.ts +3873 -795
  34. package/dist/index.js +292 -4
  35. package/dist/index.js.map +1 -1
  36. package/dist/picker-6HRMETE7.js +12 -0
  37. package/dist/picker-6HRMETE7.js.map +1 -0
  38. package/package.json +3 -1
  39. package/plugin/tracevals/.claude-plugin/plugin.json +9 -0
  40. package/plugin/tracevals/hooks/hooks.json +17 -0
  41. package/dist/chunk-TH565O2N.js.map +0 -1
  42. package/dist/chunk-UYDRDFYF.js.map +0 -1
  43. /package/dist/{chunk-BNHQWIAE.js.map → chunk-MFDD3YGU.js.map} +0 -0
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ PROJECT_RULES_FILENAMES,
4
+ extractEvals,
5
+ listInTree,
6
+ safeRead,
7
+ segments
8
+ } from "./chunk-MOQXWXSG.js";
9
+ import {
10
+ TracevalsError
11
+ } from "./chunk-3R23C5DT.js";
12
+
13
+ // src/tracevals/artifacts/discover.ts
14
+ import {
15
+ basename,
16
+ dirname,
17
+ isAbsolute,
18
+ join,
19
+ relative,
20
+ resolve,
21
+ sep
22
+ } from "node:path";
23
+ import { stat } from "node:fs/promises";
24
+ import picomatch from "picomatch";
25
+ function excluder(globs, cwd) {
26
+ if (globs === void 0 || globs.length === 0) return () => false;
27
+ const isMatch = picomatch([...globs], { dot: true });
28
+ return (path) => {
29
+ const posix = resolve(path).split(sep).join("/");
30
+ const rel = relative(cwd, resolve(path)).split(sep).join("/");
31
+ const inside = rel !== "" && !rel.startsWith("../") && !isAbsolute(rel);
32
+ return isMatch(posix) || inside && isMatch(rel);
33
+ };
34
+ }
35
+ var SKILL_DIRS = ["skills", join("src", "skills"), join(".claude", "skills")];
36
+ var RULES_NAMES = new Set(PROJECT_RULES_FILENAMES);
37
+ function classify(path) {
38
+ const name = basename(path);
39
+ if (RULES_NAMES.has(name)) return "project-rules";
40
+ if (name === "SKILL.md") return "skill";
41
+ if (!name.endsWith(".md")) return void 0;
42
+ if (segments(dirname(path)).includes("commands")) return "slash-command";
43
+ if (segments(dirname(path)).at(-1) === "agents") return "agent";
44
+ return void 0;
45
+ }
46
+ function relativeSegments(anchor, path) {
47
+ const rel = relative(resolve(anchor), resolve(path));
48
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return null;
49
+ return segments(rel);
50
+ }
51
+ function nameFor(type, path) {
52
+ if (type === "skill") return segments(dirname(path)).at(-1) ?? basename(path);
53
+ if (type === "agent" || type === "slash-command") {
54
+ return basename(path).replace(/\.md$/, "");
55
+ }
56
+ return basename(path);
57
+ }
58
+ function isRecognizedSlashCommand(path, anchor) {
59
+ const parts = relativeSegments(anchor, path);
60
+ if (parts === null) return false;
61
+ return parts.some(
62
+ (part, at) => part === "commands" && at >= 1 && parts[at - 1] === ".claude" && at < parts.length - 1
63
+ );
64
+ }
65
+ function isRecognizedSkill(path, anchor) {
66
+ const parts = relativeSegments(anchor, path);
67
+ if (parts === null) return false;
68
+ return SKILL_DIRS.some((dir) => {
69
+ const want = segments(dir);
70
+ const at = parts.length - want.length - 2;
71
+ if (at < 0) return false;
72
+ return want.every((part, i) => parts[at + i] === part);
73
+ });
74
+ }
75
+ function isRecognizedAgent(path, anchor) {
76
+ const parts = relativeSegments(anchor, path);
77
+ if (parts === null) return false;
78
+ if (parts.length === 2 && parts[0] === "agents") return true;
79
+ return parts.length >= 3 && parts.at(-3) === ".claude" && parts.at(-2) === "agents";
80
+ }
81
+ async function readOne(path, type, metadataFor) {
82
+ const content = await safeRead(path);
83
+ if (content === null) return null;
84
+ const artifact = {
85
+ name: nameFor(type, path),
86
+ type,
87
+ path,
88
+ content,
89
+ origin: "project"
90
+ };
91
+ try {
92
+ const extracted = extractEvals(artifact, await metadataFor?.(artifact));
93
+ if (extracted.errors.length > 0) {
94
+ const first = extracted.errors[0];
95
+ return {
96
+ artifact,
97
+ status: "invalid",
98
+ existingNames: [],
99
+ skip: false,
100
+ error: `metadata.evals is invalid: ${first?.message ?? "schema error"}`
101
+ };
102
+ }
103
+ return {
104
+ artifact,
105
+ status: "ok",
106
+ existingNames: extracted.evals.map((e) => e.id),
107
+ skip: extracted.skip
108
+ };
109
+ } catch (err) {
110
+ if (err instanceof TracevalsError) throw err;
111
+ return {
112
+ artifact,
113
+ status: "unreadable",
114
+ existingNames: [],
115
+ skip: false,
116
+ error: err instanceof Error ? err.message : String(err)
117
+ };
118
+ }
119
+ }
120
+ async function discoverArtifacts(options) {
121
+ const root = resolve(options.root);
122
+ const warnings = [];
123
+ const cwd = options.cwd ?? process.cwd();
124
+ const targets = options.paths?.length ? options.paths.map((p) => isAbsolute(p) ? p : resolve(cwd, p)) : [root];
125
+ const files = /* @__PURE__ */ new Map();
126
+ for (const target of targets) {
127
+ let info;
128
+ try {
129
+ info = await stat(target);
130
+ } catch {
131
+ throw new TracevalsError(`no such file or directory: ${target}`);
132
+ }
133
+ if (info.isFile()) {
134
+ files.set(target, classify(target) ?? "project-rules");
135
+ continue;
136
+ }
137
+ const found = await listInTree(target, (path) => {
138
+ const type = classify(path);
139
+ if (type === void 0) return false;
140
+ if (type === "skill") return isRecognizedSkill(path, target);
141
+ if (type === "agent") return isRecognizedAgent(path, target);
142
+ if (type === "slash-command") {
143
+ return isRecognizedSlashCommand(path, target);
144
+ }
145
+ return true;
146
+ });
147
+ for (const path of found) {
148
+ const type = classify(path);
149
+ if (type !== void 0) files.set(path, type);
150
+ }
151
+ }
152
+ const excluded = excluder(options.exclude, cwd);
153
+ for (const path of [...files.keys()]) {
154
+ if (excluded(path)) files.delete(path);
155
+ }
156
+ const artifacts = [];
157
+ for (const [path, type] of files) {
158
+ const discovered = await readOne(path, type, options.metadataFor);
159
+ if (discovered === null) {
160
+ warnings.push(`could not read ${path}`);
161
+ continue;
162
+ }
163
+ if (discovered.status !== "ok") {
164
+ warnings.push(`${path}: ${discovered.error ?? discovered.status}`);
165
+ }
166
+ artifacts.push(discovered);
167
+ }
168
+ return { artifacts, warnings };
169
+ }
170
+
171
+ export {
172
+ discoverArtifacts
173
+ };
174
+ //# sourceMappingURL=chunk-QROCFW5S.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tracevals/artifacts/discover.ts"],"sourcesContent":["/**\n * Static artifact discovery: find every skill, agent definition, and\n * project-rules file in a project without a trace.\n *\n * This is the inverse of `resolveArtifacts`, which maps names a trace *used*\n * onto files. Authoring (`manni tracevals fill`) needs the whole population instead,\n * including artifacts no session has exercised yet.\n *\n * Scope is deliberately the project only — never the user's `~/.claude` or the\n * plugin store, which hold third-party files that are not this project's to\n * edit.\n */\nimport {\n basename,\n dirname,\n isAbsolute,\n join,\n relative,\n resolve,\n sep,\n} from \"node:path\";\nimport { stat } from \"node:fs/promises\";\nimport picomatch from \"picomatch\";\nimport type { ExtractedMetadata } from \"../../meta/index.js\";\nimport { extractEvals } from \"../evals/extract.js\";\nimport { TracevalsError } from \"../types.js\";\nimport { listInTree, safeRead, segments } from \"./fs.js\";\nimport { PROJECT_RULES_FILENAMES } from \"./resolve.js\";\nimport type { ArtifactType, ResolvedArtifact } from \"./types.js\";\n\nexport interface DiscoveredArtifact {\n artifact: ResolvedArtifact;\n /**\n * `ok` — frontmatter parsed; `unreadable` — the YAML itself is malformed;\n * `invalid` — a `metadata.evals` block that fails the published schema.\n * Only `ok` artifacts are safe to propose against.\n */\n status: \"ok\" | \"unreadable\" | \"invalid\";\n /** Criterion names already declared, for dedupe and cache keying. */\n existingNames: string[];\n /** The artifact opted out via `metadata.evals.skip`. */\n skip: boolean;\n error?: string;\n}\n\nexport interface DiscoveryResult {\n artifacts: DiscoveredArtifact[];\n warnings: string[];\n}\n\nexport interface DiscoverOptions {\n /** Project root to scan. */\n root: string;\n /**\n * Explicit files or directories. A file is taken as an artifact regardless\n * of naming convention; a directory is scanned. Defaults to scanning `root`.\n * Relative entries resolve against `cwd`, like any CLI path argument.\n */\n paths?: string[];\n /** Base for relative `paths`; defaults to the process working directory. */\n cwd?: string;\n /**\n * `--exclude <glob>`, repeatable and never split on commas. Applied to\n * everything the scan found — an explicitly named file as much as a walked\n * directory — so one flag means the same thing however the artifact was\n * reached.\n */\n exclude?: string[];\n /**\n * The artifact's front matter as the run reads it: its own, or its own with a\n * manifest-supplied `metadata` block merged in (`evals/external.ts`). Without\n * it a relocated artifact looks empty here, and `fill` would propose evals it\n * already declares. Absent when no collection declares such a manifest.\n */\n metadataFor?: (artifact: ResolvedArtifact) => Promise<ExtractedMetadata | undefined>;\n}\n\n/**\n * Whether `path` is excluded by any of `globs`.\n *\n * Matched against the path as the user would write it, relative to `cwd` with\n * posix separators, so an exclude written against `test/fixtures` reads the\n * same on Windows as on Linux. A target outside `cwd` has no such spelling,\n * so the absolute posix path is offered too rather than a `../../` one no\n * glob would be written against.\n */\nfunction excluder(\n globs: readonly string[] | undefined,\n cwd: string,\n): (path: string) => boolean {\n if (globs === undefined || globs.length === 0) return () => false;\n const isMatch = picomatch([...globs], { dot: true });\n return (path) => {\n const posix = resolve(path).split(sep).join(\"/\");\n const rel = relative(cwd, resolve(path)).split(sep).join(\"/\");\n const inside = rel !== \"\" && !rel.startsWith(\"../\") && !isAbsolute(rel);\n return isMatch(posix) || (inside && isMatch(rel));\n };\n}\n\nconst SKILL_DIRS = [\"skills\", join(\"src\", \"skills\"), join(\".claude\", \"skills\")];\nconst RULES_NAMES = new Set<string>(PROJECT_RULES_FILENAMES);\n\n/** Path segments, separator-normalized, for convention matching. */\n/** Which artifact type a path represents by convention, if any. */\nfunction classify(path: string): ArtifactType | undefined {\n const name = basename(path);\n if (RULES_NAMES.has(name)) return \"project-rules\";\n if (name === \"SKILL.md\") return \"skill\";\n if (!name.endsWith(\".md\")) return undefined;\n // Candidates only — `isRecognizedSlashCommand` and `isRecognizedAgent`\n // decide whether this particular directory is one the resolver would ever\n // look in. Commands nest (an organizing subdirectory does not rename the\n // command), so any `commands` ancestor is a candidate; agents do not.\n if (segments(dirname(path)).includes(\"commands\")) return \"slash-command\";\n if (segments(dirname(path)).at(-1) === \"agents\") return \"agent\";\n return undefined;\n}\n\n/**\n * `path` split into segments relative to `anchor`, or null when it escapes.\n * Slicing by prefix length silently mangles paths that are not underneath the\n * anchor, so relative() does the work.\n */\nfunction relativeSegments(anchor: string, path: string): string[] | null {\n const rel = relative(resolve(anchor), resolve(path));\n if (rel === \"\" || rel.startsWith(\"..\") || isAbsolute(rel)) return null;\n return segments(rel);\n}\n\n/**\n * Artifact name as the rest of the system refers to it: a skill by its\n * directory, an agent by its filename stem, project rules by filename.\n */\nfunction nameFor(type: ArtifactType, path: string): string {\n if (type === \"skill\") return segments(dirname(path)).at(-1) ?? basename(path);\n // A slash command is referenced by its filename stem with the leading slash\n // stripped, and an organizing subdirectory does not enter the name — so\n // `commands/release/tag.md` is `tag`, the same string the trace records.\n if (type === \"agent\" || type === \"slash-command\") {\n return basename(path).replace(/\\.md$/, \"\");\n }\n return basename(path);\n}\n\n/**\n * Slash commands live in `.claude/commands/` at any depth — the only place\n * `resolveArtifacts` looks for a project command, so discovery and resolution\n * agree on the same population.\n *\n * Deliberately narrow, for the reason `isRecognizedAgent` is: `fill` writes by\n * default, and matching any directory named `commands` would classify a docs\n * page about a CLI as an instruction artifact and edit it.\n */\nfunction isRecognizedSlashCommand(path: string, anchor: string): boolean {\n const parts = relativeSegments(anchor, path);\n if (parts === null) return false;\n // Any `.claude/commands/` pair, not the first segment that happens to be\n // named \"commands\": a project with a top-level `commands/` directory would\n // otherwise fail the `at >= 1` test and have its real command files rejected.\n return parts.some(\n (part, at) =>\n part === \"commands\" &&\n at >= 1 &&\n parts[at - 1] === \".claude\" &&\n at < parts.length - 1,\n );\n}\n\n/** A skill file must sit as `<skillsDir>/<skillName>/SKILL.md`. */\nfunction isRecognizedSkill(path: string, anchor: string): boolean {\n const parts = relativeSegments(anchor, path);\n if (parts === null) return false;\n return SKILL_DIRS.some((dir) => {\n const want = segments(dir);\n const at = parts.length - want.length - 2;\n if (at < 0) return false;\n return want.every((part, i) => parts[at + i] === part);\n });\n}\n\n/**\n * Agent definitions live in `.claude/agents/` at any depth, or in `agents/`\n * directly at the project root — the two places `resolveArtifacts` looks.\n *\n * This is deliberately narrow. Matching any directory named `agents` would\n * classify ordinary prose in `docs/agents/` as an agent definition, and `fill`\n * writes by default, so the mistake would edit unrelated files.\n */\nfunction isRecognizedAgent(path: string, anchor: string): boolean {\n const parts = relativeSegments(anchor, path);\n if (parts === null) return false;\n if (parts.length === 2 && parts[0] === \"agents\") return true;\n return (\n parts.length >= 3 &&\n parts.at(-3) === \".claude\" &&\n parts.at(-2) === \"agents\"\n );\n}\n\nasync function readOne(\n path: string,\n type: ArtifactType,\n metadataFor?: DiscoverOptions[\"metadataFor\"],\n): Promise<DiscoveredArtifact | null> {\n const content = await safeRead(path);\n if (content === null) return null;\n const artifact: ResolvedArtifact = {\n name: nameFor(type, path),\n type,\n path,\n content,\n origin: \"project\",\n };\n\n try {\n const extracted = extractEvals(artifact, await metadataFor?.(artifact));\n if (extracted.errors.length > 0) {\n const first = extracted.errors[0];\n return {\n artifact,\n status: \"invalid\",\n existingNames: [],\n skip: false,\n error: `metadata.evals is invalid: ${first?.message ?? \"schema error\"}`,\n };\n }\n return {\n artifact,\n status: \"ok\",\n existingNames: extracted.evals.map((e) => e.id),\n skip: extracted.skip,\n };\n } catch (err) {\n // A manifest that cannot be read, or an artifact two collections both keep\n // `metadata` for, is the run's problem rather than this artifact's.\n if (err instanceof TracevalsError) throw err;\n // docmeta throws on malformed YAML. A half-written artifact must not take\n // down a scan of the whole project.\n return {\n artifact,\n status: \"unreadable\",\n existingNames: [],\n skip: false,\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nexport async function discoverArtifacts(\n options: DiscoverOptions,\n): Promise<DiscoveryResult> {\n const root = resolve(options.root);\n const warnings: string[] = [];\n const cwd = options.cwd ?? process.cwd();\n const targets = options.paths?.length\n ? options.paths.map((p) => (isAbsolute(p) ? p : resolve(cwd, p)))\n : [root];\n\n const files = new Map<string, ArtifactType>();\n\n for (const target of targets) {\n let info;\n try {\n info = await stat(target);\n } catch {\n throw new TracevalsError(`no such file or directory: ${target}`);\n }\n\n if (info.isFile()) {\n // An explicit file is taken at its word; convention only supplies the\n // type, and an unrecognized name is treated as project rules.\n files.set(target, classify(target) ?? \"project-rules\");\n continue;\n }\n\n // Convention is anchored at the directory being scanned, so\n // `fill packages/api` treats that package as its own project rather than\n // silently finding nothing.\n const found = await listInTree(target, (path) => {\n const type = classify(path);\n if (type === undefined) return false;\n if (type === \"skill\") return isRecognizedSkill(path, target);\n if (type === \"agent\") return isRecognizedAgent(path, target);\n if (type === \"slash-command\") {\n return isRecognizedSlashCommand(path, target);\n }\n return true;\n });\n for (const path of found) {\n const type = classify(path);\n if (type !== undefined) files.set(path, type);\n }\n }\n\n // After the walk rather than inside it: a `--exclude` is about the artifact\n // set, and applying it to directory names during the walk would make\n // `**/fixtures/**` and `**/fixtures` mean different things.\n const excluded = excluder(options.exclude, cwd);\n for (const path of [...files.keys()]) {\n if (excluded(path)) files.delete(path);\n }\n\n const artifacts: DiscoveredArtifact[] = [];\n for (const [path, type] of files) {\n const discovered = await readOne(path, type, options.metadataFor);\n if (discovered === null) {\n warnings.push(`could not read ${path}`);\n continue;\n }\n if (discovered.status !== \"ok\") {\n warnings.push(`${path}: ${discovered.error ?? discovered.status}`);\n }\n artifacts.push(discovered);\n }\n return { artifacts, warnings };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,379 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ assertProviderSelection,
4
+ renderCalibration,
5
+ resolveBatchTraces,
6
+ runBatch,
7
+ selectProvider
8
+ } from "./chunk-CI2GOU3A.js";
9
+ import {
10
+ discoverConfig,
11
+ labelKey,
12
+ loadLabels
13
+ } from "./chunk-MOQXWXSG.js";
14
+ import {
15
+ TracevalsError
16
+ } from "./chunk-3R23C5DT.js";
17
+
18
+ // src/tracevals/commands/calibrate.ts
19
+ import { writeFile } from "node:fs/promises";
20
+ import { basename, resolve } from "node:path";
21
+
22
+ // src/tracevals/calibrate/score.ts
23
+ import { computeConsensus, zoneFor } from "@hawkeyexl/inference";
24
+ function classify(expected, actual) {
25
+ if (actual === "skipped") return "skipped";
26
+ if (actual === "error") return "error";
27
+ if (actual === expected) return "agree";
28
+ if (expected === "fail" && actual === "pass") return "false-pass";
29
+ if (expected === "pass" && actual === "fail") return "false-fail";
30
+ if (actual === "needs-review") return "review";
31
+ return "missed-review";
32
+ }
33
+ function joinLabels(outcomes, labels) {
34
+ const byType = /* @__PURE__ */ new Map();
35
+ const byName = /* @__PURE__ */ new Map();
36
+ for (const outcome of outcomes) {
37
+ if (!("report" in outcome)) continue;
38
+ for (const result of outcome.report.evalResults) {
39
+ byType.set(
40
+ labelKey(
41
+ outcome.file,
42
+ result.artifactName,
43
+ result.evalName,
44
+ result.artifactType
45
+ ),
46
+ result
47
+ );
48
+ const key = labelKey(outcome.file, result.artifactName, result.evalName);
49
+ const seen = byName.get(key);
50
+ if (seen === void 0) byName.set(key, [result]);
51
+ else seen.push(result);
52
+ }
53
+ }
54
+ const joined = [];
55
+ const unmatched = [];
56
+ const ambiguous = [];
57
+ for (const label of labels) {
58
+ if (label.type !== void 0) {
59
+ const result = byType.get(
60
+ labelKey(label.traceFile, label.artifact, label.eval, label.type)
61
+ );
62
+ if (result === void 0) unmatched.push(label);
63
+ else joined.push({ label, result });
64
+ continue;
65
+ }
66
+ const candidates = byName.get(labelKey(label.traceFile, label.artifact, label.eval)) ?? [];
67
+ const kinds = new Set(candidates.map((r) => r.artifactType));
68
+ if (candidates.length === 0) unmatched.push(label);
69
+ else if (kinds.size > 1) ambiguous.push({ label, candidates });
70
+ else joined.push({ label, result: candidates[0] });
71
+ }
72
+ return { joined, unmatched, ambiguous };
73
+ }
74
+ var summarise = (consensus) => ({
75
+ votes: consensus.votes,
76
+ agreement: consensus.agreement,
77
+ meanConfidence: consensus.meanConfidence,
78
+ runs: consensus.runs.length
79
+ });
80
+ function rescore(result, setting) {
81
+ const consensus = result.consensus;
82
+ if (consensus === void 0) {
83
+ return { outcome: result.outcome, insufficient: false };
84
+ }
85
+ const runs = consensus.runs;
86
+ if (runs.length < setting.ensembleRuns) {
87
+ return { outcome: result.outcome, insufficient: true };
88
+ }
89
+ const slice = runs.slice(0, setting.ensembleRuns);
90
+ const base = computeConsensus(slice);
91
+ const zone = zoneFor(base, setting.zones);
92
+ return {
93
+ outcome: zone === "auto-pass" ? "pass" : zone === "auto-fail" ? "fail" : "needs-review",
94
+ insufficient: false,
95
+ consensus: {
96
+ votes: base.votes,
97
+ agreement: base.agreement,
98
+ meanConfidence: base.meanConfidence,
99
+ runs: slice.length
100
+ }
101
+ };
102
+ }
103
+ var emptyCounts = () => ({
104
+ labels: 0,
105
+ scored: 0,
106
+ agree: 0,
107
+ falsePass: 0,
108
+ falseFail: 0,
109
+ review: 0,
110
+ missedReview: 0,
111
+ errored: 0,
112
+ skipped: 0,
113
+ reviewVolume: 0,
114
+ insufficient: 0,
115
+ agreement: null
116
+ });
117
+ function score(input, setting) {
118
+ const counts = emptyCounts();
119
+ const disagreements = [];
120
+ const unscored = [];
121
+ for (const result of input.all) {
122
+ const outcome = setting === void 0 ? result.outcome : rescore(result, setting).outcome;
123
+ if (outcome === "needs-review") counts.reviewVolume += 1;
124
+ }
125
+ for (const { label, result } of input.joined) {
126
+ const scored = setting === void 0 ? { outcome: result.outcome, insufficient: false } : rescore(result, setting);
127
+ if (scored.insufficient) counts.insufficient += 1;
128
+ const kind = classify(label.expected, scored.outcome);
129
+ counts.labels += 1;
130
+ const row = {
131
+ trace: label.traceFile,
132
+ artifactType: result.artifactType,
133
+ artifactName: result.artifactName,
134
+ evalName: result.evalName,
135
+ grader: result.grader,
136
+ expected: label.expected,
137
+ actual: scored.outcome,
138
+ kind,
139
+ ...label.note !== void 0 ? { note: label.note } : {},
140
+ ...result.skipReason !== void 0 ? { skipReason: result.skipReason } : {},
141
+ // At the setting being scored, falling back to the run's own numbers
142
+ // when nothing was re-scored.
143
+ ...(() => {
144
+ const c = scored.consensus ?? (result.consensus !== void 0 ? summarise(result.consensus) : void 0);
145
+ return c !== void 0 ? { consensus: c } : {};
146
+ })()
147
+ };
148
+ switch (kind) {
149
+ case "skipped":
150
+ counts.skipped += 1;
151
+ unscored.push(row);
152
+ break;
153
+ case "agree":
154
+ counts.agree += 1;
155
+ counts.scored += 1;
156
+ break;
157
+ case "false-pass":
158
+ counts.falsePass += 1;
159
+ counts.scored += 1;
160
+ disagreements.push(row);
161
+ break;
162
+ case "false-fail":
163
+ counts.falseFail += 1;
164
+ counts.scored += 1;
165
+ disagreements.push(row);
166
+ break;
167
+ case "review":
168
+ counts.review += 1;
169
+ counts.scored += 1;
170
+ disagreements.push(row);
171
+ break;
172
+ case "missed-review":
173
+ counts.missedReview += 1;
174
+ counts.scored += 1;
175
+ disagreements.push(row);
176
+ break;
177
+ case "error":
178
+ counts.errored += 1;
179
+ counts.scored += 1;
180
+ disagreements.push(row);
181
+ break;
182
+ }
183
+ }
184
+ counts.agreement = counts.scored > 0 ? counts.agree / counts.scored : null;
185
+ const byRow = (a, b) => {
186
+ const ka = `${a.kind} ${a.trace} ${a.artifactName} ${a.evalName}`;
187
+ const kb = `${b.kind} ${b.trace} ${b.artifactName} ${b.evalName}`;
188
+ return ka < kb ? -1 : ka > kb ? 1 : 0;
189
+ };
190
+ disagreements.sort(byRow);
191
+ unscored.sort(byRow);
192
+ return { counts, disagreements, unscored };
193
+ }
194
+
195
+ // src/tracevals/commands/calibrate.ts
196
+ function sweepDepth(grid, baseline) {
197
+ return Math.max(baseline, ...grid);
198
+ }
199
+ async function runCalibrate(options) {
200
+ const start = Date.now();
201
+ const { config, dir: configDir } = await discoverConfig(
202
+ options.configDir ?? process.cwd(),
203
+ {
204
+ ...options.config === void 0 ? {} : { configPath: options.config },
205
+ ...options.noConfig === void 0 ? {} : { noConfig: options.noConfig }
206
+ }
207
+ );
208
+ assertProviderSelection(
209
+ selectProvider(config, {
210
+ ...options.provider !== void 0 ? { provider: options.provider } : {},
211
+ ...options.model !== void 0 ? { model: options.model } : {},
212
+ ...options.local !== void 0 ? { local: options.local } : {}
213
+ })
214
+ );
215
+ const labelsFile = options.labels !== void 0 ? resolve(options.labels) : resolve(configDir, config.calibrate.labels);
216
+ const labels = await loadLabels(labelsFile);
217
+ const corpus = (await resolveBatchTraces(options)).map((f) => resolve(f));
218
+ assertLabelsInCorpus(labels, corpus, labelsFile);
219
+ const baseline = {
220
+ ensembleRuns: options.runs ?? config.judge.ensembleRuns,
221
+ zones: config.judge.zones
222
+ };
223
+ const grid = config.calibrate.sweep;
224
+ const depth = options.sweep === true ? sweepDepth(grid.ensembleRuns, baseline.ensembleRuns) : baseline.ensembleRuns;
225
+ const batch = await runBatch({
226
+ ...options,
227
+ traces: corpus,
228
+ allProjects: void 0,
229
+ newerThan: void 0,
230
+ limit: void 0,
231
+ runs: depth,
232
+ // The calibration report is the deliverable; rendering the batch as well
233
+ // would write the wrong thing to --output.
234
+ format: "json",
235
+ output: void 0
236
+ });
237
+ const all = [];
238
+ for (const outcome of batch.outcomes) {
239
+ if ("report" in outcome) all.push(...outcome.report.evalResults);
240
+ }
241
+ const { joined, unmatched, ambiguous } = joinLabels(batch.outcomes, labels);
242
+ assertNoUnmatched(unmatched, batch, labelsFile);
243
+ assertNoAmbiguous(ambiguous, labelsFile);
244
+ const scored = score(
245
+ { joined, all },
246
+ options.sweep === true ? baseline : void 0
247
+ );
248
+ const sweep = options.sweep === true ? buildSweep({ joined, all }, baseline, grid) : void 0;
249
+ const gates = buildGates(scored.counts, {
250
+ falsePass: options.maxFalsePass ?? config.calibrate.maxFalsePass,
251
+ falseFail: options.maxFalseFail ?? config.calibrate.maxFalseFail,
252
+ review: options.maxReview ?? config.calibrate.maxReview
253
+ });
254
+ const warnings = [...batch.report.warnings];
255
+ if (scored.counts.insufficient > 0) {
256
+ warnings.push(
257
+ `${scored.counts.insufficient} labelled eval(s) had fewer cached runs than the setting asks for and were left at their reported outcome`
258
+ );
259
+ }
260
+ const measuredNothing = scored.counts.scored === 0;
261
+ if (measuredNothing) {
262
+ warnings.push(
263
+ `no labelled eval produced evidence: ${scored.counts.labels} label(s) all joined to a skipped result, so agreement and every --max-* threshold are computed over an empty denominator`
264
+ );
265
+ }
266
+ const report = {
267
+ labelsFile,
268
+ corpus,
269
+ setting: baseline,
270
+ counts: scored.counts,
271
+ disagreements: scored.disagreements,
272
+ unscored: scored.unscored,
273
+ ...sweep !== void 0 ? { sweep } : {},
274
+ gates,
275
+ batch: batch.report,
276
+ warnings,
277
+ // A calibration run is a measurement, not a gate: disagreement is the
278
+ // finding, not a failure. Exit 1 only when a threshold was asked for and
279
+ // missed, or when the measurement itself is incomplete — a trace lost, the
280
+ // shared judge budget cut the corpus short, or nothing scored at all. An
281
+ // incomplete number presented as a clean one is worse than no number.
282
+ exitCode: gates.some((g) => g.exceeded) || batch.report.summary.tracesErrored > 0 || batch.report.budget !== void 0 || measuredNothing ? 1 : 0,
283
+ turns: batch.report.turns,
284
+ durationMs: Date.now() - start
285
+ };
286
+ const rendered = renderCalibration(report, options.format ?? "pretty", {
287
+ color: options.color === true
288
+ });
289
+ if (options.output) await writeFile(options.output, rendered, "utf-8");
290
+ return { report, rendered };
291
+ }
292
+ function assertLabelsInCorpus(labels, corpus, labelsFile) {
293
+ const known = new Set(corpus);
294
+ const missing = [...new Set(labels.filter((l) => !known.has(l.traceFile)).map((l) => l.trace))];
295
+ if (missing.length === 0) return;
296
+ throw new TracevalsError(
297
+ `${labelsFile}: label(s) name trace(s) not in the corpus: ${missing.join(", ")}. The corpus is: ${corpus.map((f) => basename(f)).join(", ")}`
298
+ );
299
+ }
300
+ function assertNoUnmatched(unmatched, batch, labelsFile) {
301
+ if (unmatched.length === 0) return;
302
+ const lines = unmatched.map((label) => {
303
+ const outcome = batch.outcomes.find((o) => o.file === label.traceFile);
304
+ const results = outcome?.report?.evalResults ?? [];
305
+ const sameArtifact = results.filter(
306
+ (r) => r.artifactName === label.artifact
307
+ );
308
+ const hint = sameArtifact.length > 0 ? `that trace evaluated ${sameArtifact.map((r) => r.evalName).sort().join(", ")} for "${label.artifact}"` : `that trace resolved no artifact named "${label.artifact}" \u2014 it evaluated ${[...new Set(results.map((r) => r.artifactName))].sort().join(", ") || "nothing"}`;
309
+ return ` ${label.artifact} \u203A ${label.eval} on ${basename(label.traceFile)} (${hint})`;
310
+ });
311
+ throw new TracevalsError(
312
+ `${labelsFile}: label(s) matched no eval result:
313
+ ${lines.join("\n")}`
314
+ );
315
+ }
316
+ function assertNoAmbiguous(ambiguous, labelsFile) {
317
+ if (ambiguous.length === 0) return;
318
+ const lines = ambiguous.map(({ label, candidates }) => {
319
+ const kinds = [...new Set(candidates.map((r) => r.artifactType))].sort();
320
+ return ` ${label.artifact} \u203A ${label.eval} on ${basename(label.traceFile)} matches ${kinds.join(" and ")} \u2014 add \`type:\` to say which`;
321
+ });
322
+ throw new TracevalsError(
323
+ `${labelsFile}: label(s) name an artifact two kinds answer:
324
+ ${lines.join("\n")}`
325
+ );
326
+ }
327
+ function buildGates(counts, limits) {
328
+ const gates = [];
329
+ const add = (name, limit, actual) => {
330
+ if (limit === void 0) return;
331
+ gates.push({ name, limit, actual, exceeded: actual > limit });
332
+ };
333
+ add("falsePass", limits.falsePass, counts.falsePass);
334
+ add("falseFail", limits.falseFail, counts.falseFail);
335
+ add("review", limits.review, counts.reviewVolume);
336
+ return gates;
337
+ }
338
+ function buildSweep(input, baseline, grid) {
339
+ const cell = (axis, value, setting) => ({
340
+ axis,
341
+ value,
342
+ setting,
343
+ counts: score(input, setting).counts
344
+ });
345
+ const cells = [
346
+ cell("baseline", baseline.ensembleRuns, baseline)
347
+ ];
348
+ for (const runs of [...grid.ensembleRuns].sort((a, b) => a - b)) {
349
+ cells.push(
350
+ cell("ensembleRuns", runs, { ensembleRuns: runs, zones: baseline.zones })
351
+ );
352
+ }
353
+ for (const autoPass of [...grid.autoPass].sort((a, b) => a - b)) {
354
+ cells.push(
355
+ cell("zones.autoPass", autoPass, {
356
+ ensembleRuns: baseline.ensembleRuns,
357
+ zones: { autoPass, autoFail: baseline.zones.autoFail }
358
+ })
359
+ );
360
+ }
361
+ for (const autoFail of [...grid.autoFail].sort((a, b) => a - b)) {
362
+ cells.push(
363
+ cell("zones.autoFail", autoFail, {
364
+ ensembleRuns: baseline.ensembleRuns,
365
+ zones: { autoPass: baseline.zones.autoPass, autoFail }
366
+ })
367
+ );
368
+ }
369
+ return cells;
370
+ }
371
+
372
+ export {
373
+ classify,
374
+ joinLabels,
375
+ rescore,
376
+ score,
377
+ runCalibrate
378
+ };
379
+ //# sourceMappingURL=chunk-RVVCA4WL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tracevals/commands/calibrate.ts","../src/tracevals/calibrate/score.ts"],"sourcesContent":["/**\n * `manni tracevals calibrate <traces...> --labels <file>` (ADR 01022).\n *\n * The tool could report what it decided; it could not report whether it was\n * right. This command runs the batch (ADR 01018), joins the results against a\n * human's answers, and reports the three numbers the calibration page used to\n * ask people to count by hand — false passes, false fails, and review volume —\n * plus which eval disagreed.\n *\n * `--sweep` costs no inference beyond the one run it already made: a judged\n * eval's consensus carries the whole `JudgeRun[]`, so a different zone\n * threshold or a smaller ensemble is arithmetic over verdicts already on disk.\n * Read-only throughout — labels, traces, and artifacts are never written.\n */\nimport { writeFile } from \"node:fs/promises\";\nimport { basename, resolve } from \"node:path\";\nimport { discoverConfig } from \"../core/config.js\";\nimport { assertProviderSelection, selectProvider } from \"../judge/provider.js\";\nimport { renderCalibration, type ReportFormat } from \"../reporters/index.js\";\nimport { TracevalsError, type EvalResult } from \"../types.js\";\nimport { loadLabels, type EvalLabel } from \"../calibrate/labels.js\";\nimport { joinLabels, score, type AmbiguousLabel } from \"../calibrate/score.js\";\nimport type {\n CalibrationGate,\n CalibrationReport,\n JudgeSetting,\n SweepCell,\n} from \"../calibrate/types.js\";\nimport {\n resolveBatchTraces,\n runBatch,\n type BatchCommandOptions,\n} from \"./batch.js\";\n\nexport interface CalibrateCommandOptions extends BatchCommandOptions {\n /** Overrides `calibrate.labels`; resolved against the working directory. */\n labels?: string;\n /** Re-score the corpus across the configured grid. */\n sweep?: boolean;\n /** Thresholds; undefined defers to the config, which defaults to no gate. */\n maxFalsePass?: number;\n maxFalseFail?: number;\n maxReview?: number;\n}\n\nexport interface CalibrateCommandResult {\n report: CalibrationReport;\n rendered: string;\n}\n\n/** Largest ensemble the sweep needs cached, so every cell can be re-scored. */\nfunction sweepDepth(grid: number[], baseline: number): number {\n return Math.max(baseline, ...grid);\n}\n\nexport async function runCalibrate(\n options: CalibrateCommandOptions,\n): Promise<CalibrateCommandResult> {\n const start = Date.now();\n const { config, dir: configDir } = await discoverConfig(\n options.configDir ?? process.cwd(),\n {\n ...(options.config === undefined ? {} : { configPath: options.config }),\n ...(options.noConfig === undefined ? {} : { noConfig: options.noConfig }),\n },\n );\n\n // Before the labels file is read: a contradictory `--local --provider`, an\n // unknown provider name, or a model with no provider to own it is a usage\n // error, and a usage error must not arrive behind an unrelated one about a\n // file the run would never have needed.\n assertProviderSelection(\n selectProvider(config, {\n ...(options.provider !== undefined ? { provider: options.provider } : {}),\n ...(options.model !== undefined ? { model: options.model } : {}),\n ...(options.local !== undefined ? { local: options.local } : {}),\n }),\n );\n\n // A flag overrides the config rather than bypassing it (CLAUDE.md,\n // \"Config <-> CLI flags\"). An explicit --labels is a working-directory path,\n // the way every other path argument on the CLI is; the config default is a\n // project path and resolves against the config file's directory.\n const labelsFile =\n options.labels !== undefined\n ? resolve(options.labels)\n : resolve(configDir, config.calibrate.labels);\n const labels = await loadLabels(labelsFile);\n\n // Resolve the corpus once and hand the list to the batch, so the membership\n // check below cannot disagree with what actually ran.\n const corpus = (await resolveBatchTraces(options)).map((f) => resolve(f));\n assertLabelsInCorpus(labels, corpus, labelsFile);\n\n const baseline: JudgeSetting = {\n ensembleRuns: options.runs ?? config.judge.ensembleRuns,\n zones: config.judge.zones,\n };\n const grid = config.calibrate.sweep;\n // With --sweep the corpus is judged once at the deepest ensemble the grid\n // asks for, and every cell — the baseline included — is re-scored from those\n // runs. Judging at the configured depth instead would make the larger cells\n // unscoreable, and judging per cell would multiply the bill by the grid.\n const depth =\n options.sweep === true\n ? sweepDepth(grid.ensembleRuns, baseline.ensembleRuns)\n : baseline.ensembleRuns;\n\n const batch = await runBatch({\n ...options,\n traces: corpus,\n allProjects: undefined,\n newerThan: undefined,\n limit: undefined,\n runs: depth,\n // The calibration report is the deliverable; rendering the batch as well\n // would write the wrong thing to --output.\n format: \"json\",\n output: undefined,\n });\n\n const all: EvalResult[] = [];\n for (const outcome of batch.outcomes) {\n if (\"report\" in outcome) all.push(...outcome.report.evalResults);\n }\n const { joined, unmatched, ambiguous } = joinLabels(batch.outcomes, labels);\n assertNoUnmatched(unmatched, batch, labelsFile);\n assertNoAmbiguous(ambiguous, labelsFile);\n\n // Without a sweep the reported outcomes are used verbatim, so the headline\n // numbers are exactly the verdicts `run` produced. With one, the corpus was\n // judged deeper than the configured ensemble, so the baseline row has to be\n // re-scored back down to it.\n const scored = score(\n { joined, all },\n options.sweep === true ? baseline : undefined,\n );\n\n const sweep =\n options.sweep === true\n ? buildSweep({ joined, all }, baseline, grid)\n : undefined;\n\n const gates = buildGates(scored.counts, {\n falsePass: options.maxFalsePass ?? config.calibrate.maxFalsePass,\n falseFail: options.maxFalseFail ?? config.calibrate.maxFalseFail,\n review: options.maxReview ?? config.calibrate.maxReview,\n });\n\n const warnings = [...batch.report.warnings];\n if (scored.counts.insufficient > 0) {\n warnings.push(\n `${scored.counts.insufficient} labelled eval(s) had fewer cached runs than the setting asks for and were left at their reported outcome`,\n );\n }\n\n // A measurement that measured nothing must not report a pass.\n //\n // Every label joining to a `skipped` result — `--deterministic-only` over\n // ai-graded labels, a budget exhausted on the first trace — leaves `scored`\n // at 0, `agreement` at null, and each threshold reading \"0 of at most N, not\n // exceeded\". The gates are not lying; the denominator behind them is empty.\n // Saying so is the report's job, and exit 0 would say the opposite.\n const measuredNothing = scored.counts.scored === 0;\n if (measuredNothing) {\n warnings.push(\n `no labelled eval produced evidence: ${scored.counts.labels} label(s) all joined to a skipped result, ` +\n `so agreement and every --max-* threshold are computed over an empty denominator`,\n );\n }\n\n const report: CalibrationReport = {\n labelsFile,\n corpus,\n setting: baseline,\n counts: scored.counts,\n disagreements: scored.disagreements,\n unscored: scored.unscored,\n ...(sweep !== undefined ? { sweep } : {}),\n gates,\n batch: batch.report,\n warnings,\n // A calibration run is a measurement, not a gate: disagreement is the\n // finding, not a failure. Exit 1 only when a threshold was asked for and\n // missed, or when the measurement itself is incomplete — a trace lost, the\n // shared judge budget cut the corpus short, or nothing scored at all. An\n // incomplete number presented as a clean one is worse than no number.\n exitCode:\n gates.some((g) => g.exceeded) ||\n batch.report.summary.tracesErrored > 0 ||\n batch.report.budget !== undefined ||\n measuredNothing\n ? 1\n : 0,\n turns: batch.report.turns,\n durationMs: Date.now() - start,\n };\n\n const rendered = renderCalibration(report, options.format ?? \"pretty\", {\n color: options.color === true,\n });\n if (options.output) await writeFile(options.output, rendered, \"utf-8\");\n return { report, rendered };\n}\n\n/**\n * Every label must name a trace this run evaluates. Checked before the batch,\n * because the alternative is paying for a corpus and then being told the file\n * was misspelled.\n */\nfunction assertLabelsInCorpus(\n labels: EvalLabel[],\n corpus: string[],\n labelsFile: string,\n): void {\n const known = new Set(corpus);\n const missing = [...new Set(labels.filter((l) => !known.has(l.traceFile)).map((l) => l.trace))];\n if (missing.length === 0) return;\n throw new TracevalsError(\n `${labelsFile}: label(s) name trace(s) not in the corpus: ${missing.join(\", \")}. ` +\n `The corpus is: ${corpus.map((f) => basename(f)).join(\", \")}`,\n );\n}\n\n/**\n * A label that matched nothing is an error, not a quiet omission.\n *\n * It has to be: a typo'd eval id contributes to no count, so the report would\n * read as a cleaner corpus than it is — the same silent-nothing failure the\n * empty-selector rule exists to prevent (ADR 01018).\n */\nfunction assertNoUnmatched(\n unmatched: EvalLabel[],\n batch: { outcomes: { file: string; report?: { evalResults: EvalResult[] } }[] },\n labelsFile: string,\n): void {\n if (unmatched.length === 0) return;\n const lines = unmatched.map((label) => {\n const outcome = batch.outcomes.find((o) => o.file === label.traceFile);\n const results = outcome?.report?.evalResults ?? [];\n const sameArtifact = results.filter(\n (r) => r.artifactName === label.artifact,\n );\n const hint =\n sameArtifact.length > 0\n ? `that trace evaluated ${sameArtifact.map((r) => r.evalName).sort().join(\", \")} for \"${label.artifact}\"`\n : `that trace resolved no artifact named \"${label.artifact}\" — it evaluated ${\n [...new Set(results.map((r) => r.artifactName))].sort().join(\", \") ||\n \"nothing\"\n }`;\n return ` ${label.artifact} › ${label.eval} on ${basename(label.traceFile)} (${hint})`;\n });\n throw new TracevalsError(\n `${labelsFile}: label(s) matched no eval result:\\n${lines.join(\"\\n\")}`,\n );\n}\n\n/**\n * A label that omits `type:` over a name two artifact kinds both answer cannot\n * be resolved, and must not be guessed. Binding it to whichever result was\n * indexed last would score agreement against a verdict belonging to a\n * different artifact — the report would read as measured when it is wrong,\n * which is the one thing this command exists not to do.\n *\n * Operational, like `assertNoUnmatched`: the labels file is under-specified,\n * so this is exit 2 (bad input), not exit 1 (a check failed).\n */\nfunction assertNoAmbiguous(\n ambiguous: AmbiguousLabel[],\n labelsFile: string,\n): void {\n if (ambiguous.length === 0) return;\n const lines = ambiguous.map(({ label, candidates }) => {\n const kinds = [...new Set(candidates.map((r) => r.artifactType))].sort();\n return ` ${label.artifact} › ${label.eval} on ${basename(label.traceFile)} matches ${kinds.join(\" and \")} — add \\`type:\\` to say which`;\n });\n throw new TracevalsError(\n `${labelsFile}: label(s) name an artifact two kinds answer:\\n${lines.join(\"\\n\")}`,\n );\n}\n\nfunction buildGates(\n counts: { falsePass: number; falseFail: number; reviewVolume: number },\n limits: { falsePass?: number; falseFail?: number; review?: number },\n): CalibrationGate[] {\n const gates: CalibrationGate[] = [];\n const add = (\n name: CalibrationGate[\"name\"],\n limit: number | undefined,\n actual: number,\n ): void => {\n if (limit === undefined) return;\n gates.push({ name, limit, actual, exceeded: actual > limit });\n };\n add(\"falsePass\", limits.falsePass, counts.falsePass);\n add(\"falseFail\", limits.falseFail, counts.falseFail);\n add(\"review\", limits.review, counts.reviewVolume);\n return gates;\n}\n\n/**\n * One axis at a time, every other knob held at its configured value.\n *\n * A full cross-product would be dozens of rows and would answer a question\n * nobody asked; calibration is \"move one knob, re-run, recount\", and the\n * per-axis shape is what makes a row readable as *the effect of that knob*.\n */\nfunction buildSweep(\n input: Parameters<typeof score>[0],\n baseline: JudgeSetting,\n grid: { ensembleRuns: number[]; autoPass: number[]; autoFail: number[] },\n): SweepCell[] {\n const cell = (\n axis: SweepCell[\"axis\"],\n value: number,\n setting: JudgeSetting,\n ): SweepCell => ({\n axis,\n value,\n setting,\n counts: score(input, setting).counts,\n });\n\n const cells: SweepCell[] = [\n cell(\"baseline\", baseline.ensembleRuns, baseline),\n ];\n for (const runs of [...grid.ensembleRuns].sort((a, b) => a - b)) {\n cells.push(\n cell(\"ensembleRuns\", runs, { ensembleRuns: runs, zones: baseline.zones }),\n );\n }\n for (const autoPass of [...grid.autoPass].sort((a, b) => a - b)) {\n cells.push(\n cell(\"zones.autoPass\", autoPass, {\n ensembleRuns: baseline.ensembleRuns,\n zones: { autoPass, autoFail: baseline.zones.autoFail },\n }),\n );\n }\n for (const autoFail of [...grid.autoFail].sort((a, b) => a - b)) {\n cells.push(\n cell(\"zones.autoFail\", autoFail, {\n ensembleRuns: baseline.ensembleRuns,\n zones: { autoPass: baseline.zones.autoPass, autoFail },\n }),\n );\n }\n return cells;\n}\n\nexport type { ReportFormat };\n","/**\n * Joining labels to results, and re-scoring cached verdicts (ADR 01022).\n *\n * Everything here is pure: results and labels in, counts out. That is what\n * makes a sweep free. A judged eval's `ConsensusResult` carries the whole\n * `JudgeRun[]` it was built from, so a different zone threshold — or a smaller\n * ensemble — is a fresh call to `computeConsensus`/`zoneFor` over runs the\n * corpus already produced, not a fresh call to a provider.\n */\nimport { computeConsensus, zoneFor } from \"@hawkeyexl/inference\";\nimport type { BatchOutcome } from \"../aggregate.js\";\nimport type { EvalResult, Outcome } from \"../types.js\";\nimport { labelKey, type EvalLabel, type LabelOutcome } from \"./labels.js\";\nimport type {\n CalibrationCounts,\n Disagreement,\n JoinKind,\n JudgeSetting,\n} from \"./types.js\";\n\n/**\n * How one label met one result, over the full (expected × actual) grid.\n *\n * The order matters. `skipped` and `error` are tested *first*, so a labelled\n * eval that produced no evidence can never be counted as agreement — the same\n * rule that keeps an errored judge run from becoming a silent pass.\n */\nexport function classify(expected: LabelOutcome, actual: Outcome): JoinKind {\n if (actual === \"skipped\") return \"skipped\";\n if (actual === \"error\") return \"error\";\n if (actual === expected) return \"agree\";\n if (expected === \"fail\" && actual === \"pass\") return \"false-pass\";\n if (expected === \"pass\" && actual === \"fail\") return \"false-fail\";\n if (actual === \"needs-review\") return \"review\";\n return \"missed-review\";\n}\n\nexport interface JoinedLabel {\n label: EvalLabel;\n result: EvalResult;\n}\n\nexport interface JoinResult {\n joined: JoinedLabel[];\n /**\n * Labels that matched no result. Never silently dropped: a typo'd eval id\n * would otherwise deflate every count on the report by one.\n */\n unmatched: EvalLabel[];\n /**\n * Labels that omit `type:` over a name two artifact kinds both answer. Never\n * bound to one of them: a skill and the slash command that types it can share\n * a name in one trace, and picking whichever was indexed last would measure\n * agreement against a verdict belonging to something else — a false pass or\n * false fail reported with total confidence.\n */\n ambiguous: AmbiguousLabel[];\n}\n\n/** An untyped label and every result its name matched. */\nexport interface AmbiguousLabel {\n label: EvalLabel;\n candidates: EvalResult[];\n}\n\n/**\n * Match each label to the result its trace produced. The key is\n * `(trace file, artifact name, eval id)`, with the artifact *type* joining too\n * when the label states one — needed only for a skill and an agent sharing a\n * name, which is legal and does happen.\n */\nexport function joinLabels(\n outcomes: BatchOutcome[],\n labels: EvalLabel[],\n): JoinResult {\n // Typed keys are unique by construction. Untyped ones are not: two artifact\n // kinds may answer the same name, so keep every candidate rather than\n // letting the last one indexed overwrite the rest.\n const byType = new Map<string, EvalResult>();\n const byName = new Map<string, EvalResult[]>();\n for (const outcome of outcomes) {\n if (!(\"report\" in outcome)) continue;\n for (const result of outcome.report.evalResults) {\n byType.set(\n labelKey(\n outcome.file,\n result.artifactName,\n result.evalName,\n result.artifactType,\n ),\n result,\n );\n const key = labelKey(outcome.file, result.artifactName, result.evalName);\n const seen = byName.get(key);\n if (seen === undefined) byName.set(key, [result]);\n else seen.push(result);\n }\n }\n\n const joined: JoinedLabel[] = [];\n const unmatched: EvalLabel[] = [];\n const ambiguous: AmbiguousLabel[] = [];\n for (const label of labels) {\n if (label.type !== undefined) {\n const result = byType.get(\n labelKey(label.traceFile, label.artifact, label.eval, label.type),\n );\n if (result === undefined) unmatched.push(label);\n else joined.push({ label, result });\n continue;\n }\n const candidates =\n byName.get(labelKey(label.traceFile, label.artifact, label.eval)) ?? [];\n const kinds = new Set(candidates.map((r) => r.artifactType));\n if (candidates.length === 0) unmatched.push(label);\n else if (kinds.size > 1) ambiguous.push({ label, candidates });\n else joined.push({ label, result: candidates[0] as EvalResult });\n }\n return { joined, unmatched, ambiguous };\n}\n\nexport interface Rescored {\n outcome: Outcome;\n /** The cache held fewer runs than the setting asks for. */\n insufficient: boolean;\n /**\n * The arithmetic **at this setting**, not at the setting the corpus was\n * judged with. A sweep judges deeper than most of its cells, so reporting\n * the full cached ensemble beside a re-scored verdict would show a reader\n * evidence that does not add up to the outcome next to it.\n */\n consensus?: Disagreement[\"consensus\"];\n}\n\n/** The numbers a reader needs from a consensus, without the run bodies. */\nconst summarise = (\n consensus: NonNullable<EvalResult[\"consensus\"]>,\n): Disagreement[\"consensus\"] => ({\n votes: consensus.votes,\n agreement: consensus.agreement,\n meanConfidence: consensus.meanConfidence,\n runs: consensus.runs.length,\n});\n\n/**\n * What this result would have been at a different setting.\n *\n * A result with no `consensus` — deterministic, `human`, skipped, errored — is\n * returned untouched: zones and ensemble size do not reach it, and pretending\n * otherwise would make a sweep look like it moved things it cannot move.\n *\n * A smaller `ensembleRuns` re-scores the **first k** of the cached runs. That\n * is a sub-sample of the ensemble that was actually drawn, not a fresh k-run\n * ensemble; at temperature 0 the runs are independent samples of one prompt,\n * so it is an honest estimate, and the alternative is paying per grid cell.\n */\nexport function rescore(result: EvalResult, setting: JudgeSetting): Rescored {\n const consensus = result.consensus;\n if (consensus === undefined) {\n return { outcome: result.outcome, insufficient: false };\n }\n const runs = consensus.runs;\n if (runs.length < setting.ensembleRuns) {\n // Never scale up by guessing. Report the setting as unreachable and leave\n // the row at what it actually was.\n return { outcome: result.outcome, insufficient: true };\n }\n const slice = runs.slice(0, setting.ensembleRuns);\n const base = computeConsensus(slice);\n const zone = zoneFor(base, setting.zones);\n return {\n outcome:\n zone === \"auto-pass\" ? \"pass\" : zone === \"auto-fail\" ? \"fail\" : \"needs-review\",\n insufficient: false,\n consensus: {\n votes: base.votes,\n agreement: base.agreement,\n meanConfidence: base.meanConfidence,\n runs: slice.length,\n },\n };\n}\n\nconst emptyCounts = (): CalibrationCounts => ({\n labels: 0,\n scored: 0,\n agree: 0,\n falsePass: 0,\n falseFail: 0,\n review: 0,\n missedReview: 0,\n errored: 0,\n skipped: 0,\n reviewVolume: 0,\n insufficient: 0,\n agreement: null,\n});\n\nexport interface ScoreInput {\n joined: JoinedLabel[];\n /** Every eval result in the corpus, for the corpus-wide review volume. */\n all: EvalResult[];\n}\n\nexport interface ScoreResult {\n counts: CalibrationCounts;\n disagreements: Disagreement[];\n unscored: Disagreement[];\n}\n\n/**\n * Score the joined corpus, optionally at a setting other than the one it ran\n * at. Omit `setting` and the reported outcomes are used verbatim, so the\n * headline numbers of a plain `calibrate` are exactly the verdicts `run`\n * would have produced — no re-derivation standing between the two commands.\n */\nexport function score(input: ScoreInput, setting?: JudgeSetting): ScoreResult {\n const counts = emptyCounts();\n const disagreements: Disagreement[] = [];\n const unscored: Disagreement[] = [];\n\n for (const result of input.all) {\n const outcome =\n setting === undefined ? result.outcome : rescore(result, setting).outcome;\n if (outcome === \"needs-review\") counts.reviewVolume += 1;\n }\n\n for (const { label, result } of input.joined) {\n const scored: Rescored =\n setting === undefined\n ? { outcome: result.outcome, insufficient: false }\n : rescore(result, setting);\n if (scored.insufficient) counts.insufficient += 1;\n const kind = classify(label.expected, scored.outcome);\n counts.labels += 1;\n\n const row: Disagreement = {\n trace: label.traceFile,\n artifactType: result.artifactType,\n artifactName: result.artifactName,\n evalName: result.evalName,\n grader: result.grader,\n expected: label.expected,\n actual: scored.outcome,\n kind,\n ...(label.note !== undefined ? { note: label.note } : {}),\n ...(result.skipReason !== undefined\n ? { skipReason: result.skipReason }\n : {}),\n // At the setting being scored, falling back to the run's own numbers\n // when nothing was re-scored.\n ...(() => {\n const c =\n scored.consensus ??\n (result.consensus !== undefined ? summarise(result.consensus) : undefined);\n return c !== undefined ? { consensus: c } : {};\n })(),\n };\n\n switch (kind) {\n case \"skipped\":\n counts.skipped += 1;\n unscored.push(row);\n break;\n case \"agree\":\n counts.agree += 1;\n counts.scored += 1;\n break;\n case \"false-pass\":\n counts.falsePass += 1;\n counts.scored += 1;\n disagreements.push(row);\n break;\n case \"false-fail\":\n counts.falseFail += 1;\n counts.scored += 1;\n disagreements.push(row);\n break;\n case \"review\":\n counts.review += 1;\n counts.scored += 1;\n disagreements.push(row);\n break;\n case \"missed-review\":\n counts.missedReview += 1;\n counts.scored += 1;\n disagreements.push(row);\n break;\n case \"error\":\n counts.errored += 1;\n counts.scored += 1;\n disagreements.push(row);\n break;\n }\n }\n\n counts.agreement = counts.scored > 0 ? counts.agree / counts.scored : null;\n // Byte comparison, so the ubuntu and windows legs order a report the same\n // way and two runs over one corpus stay diffable.\n const byRow = (a: Disagreement, b: Disagreement): number => {\n const ka = `${a.kind} ${a.trace} ${a.artifactName} ${a.evalName}`;\n const kb = `${b.kind} ${b.trace} ${b.artifactName} ${b.evalName}`;\n return ka < kb ? -1 : ka > kb ? 1 : 0;\n };\n disagreements.sort(byRow);\n unscored.sort(byRow);\n return { counts, disagreements, unscored };\n}\n"],"mappings":"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","names":[]}