@skill-harness/core 0.11.0 → 0.13.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 (95) hide show
  1. package/README.md +2 -1
  2. package/dist/adapters/types.d.ts +19 -0
  3. package/dist/adjudication.d.ts +9 -1
  4. package/dist/adjudication.js +41 -33
  5. package/dist/adoption.d.ts +128 -0
  6. package/dist/adoption.js +102 -0
  7. package/dist/arms.d.ts +1 -1
  8. package/dist/capture-trace-types.d.ts +2 -0
  9. package/dist/command-cost.d.ts +9 -0
  10. package/dist/command-cost.js +23 -0
  11. package/dist/comparison.d.ts +1 -0
  12. package/dist/comparison.js +9 -3
  13. package/dist/execution-trace.js +9 -1
  14. package/dist/factory-calibration.d.ts +63 -0
  15. package/dist/factory-calibration.js +115 -0
  16. package/dist/grade.d.ts +2 -0
  17. package/dist/grade.js +2 -1
  18. package/dist/index.d.ts +21 -0
  19. package/dist/index.js +21 -0
  20. package/dist/intervention-results.d.ts +6 -0
  21. package/dist/intervention-results.js +52 -0
  22. package/dist/intervention.d.ts +123 -0
  23. package/dist/intervention.js +218 -0
  24. package/dist/investigation.d.ts +94 -0
  25. package/dist/investigation.js +148 -0
  26. package/dist/journal.d.ts +1 -1
  27. package/dist/judge-agreement.d.ts +19 -0
  28. package/dist/judge-agreement.js +31 -0
  29. package/dist/judge-policy.js +5 -3
  30. package/dist/lift.js +1 -1
  31. package/dist/lint.js +1 -1
  32. package/dist/prompt-normalization.d.ts +6 -0
  33. package/dist/prompt-normalization.js +14 -0
  34. package/dist/qualification-capture.d.ts +44 -0
  35. package/dist/qualification-capture.js +163 -0
  36. package/dist/qualification-config.d.ts +233 -0
  37. package/dist/qualification-config.js +694 -0
  38. package/dist/qualification-lock.d.ts +15 -0
  39. package/dist/qualification-lock.js +87 -0
  40. package/dist/qualification-oauth-directory.d.ts +79 -0
  41. package/dist/qualification-oauth-directory.js +444 -0
  42. package/dist/qualification-panel-store.d.ts +14 -0
  43. package/dist/qualification-panel-store.js +188 -0
  44. package/dist/qualification-panels.d.ts +99 -0
  45. package/dist/qualification-panels.js +91 -0
  46. package/dist/qualification-process.d.ts +13 -0
  47. package/dist/qualification-process.js +190 -0
  48. package/dist/qualification-runner.d.ts +203 -0
  49. package/dist/qualification-runner.js +2037 -0
  50. package/dist/qualification-store.d.ts +187 -0
  51. package/dist/qualification-store.js +833 -0
  52. package/dist/qualification-validation.d.ts +12 -0
  53. package/dist/qualification-validation.js +8 -0
  54. package/dist/regate.js +42 -10
  55. package/dist/regrade.d.ts +3 -1
  56. package/dist/regrade.js +37 -5
  57. package/dist/reps.d.ts +4 -2
  58. package/dist/reps.js +13 -4
  59. package/dist/rescore.js +2 -1
  60. package/dist/results.d.ts +53 -7
  61. package/dist/results.js +210 -7
  62. package/dist/run.js +66 -17
  63. package/dist/score.d.ts +4 -3
  64. package/dist/score.js +13 -4
  65. package/dist/screen.d.ts +31 -0
  66. package/dist/screen.js +105 -0
  67. package/dist/seeded.d.ts +2 -1
  68. package/dist/seeded.js +1 -0
  69. package/dist/sources.d.ts +7 -1
  70. package/dist/sources.js +13 -5
  71. package/dist/spec.d.ts +1 -1
  72. package/dist/spec.js +1 -1
  73. package/dist/stability.js +1 -1
  74. package/dist/trajectory-gates.d.ts +14 -14
  75. package/dist/trajectory-gates.js +18 -34
  76. package/dist/trusted-host-supervision.d.ts +25 -0
  77. package/dist/trusted-host-supervision.js +94 -0
  78. package/dist/vote-panel.d.ts +33 -0
  79. package/dist/vote-panel.js +41 -0
  80. package/dist/work-capture-schema.d.ts +38 -0
  81. package/dist/work-capture-schema.js +33 -0
  82. package/dist/work-capture.d.ts +43 -0
  83. package/dist/work-capture.js +81 -0
  84. package/dist/work-signals.d.ts +55 -0
  85. package/dist/work-signals.js +71 -0
  86. package/dist/workspace.js +4 -1
  87. package/package.json +3 -3
  88. package/schemas/qualification-cell-result-v1.schema.json +32 -0
  89. package/schemas/qualification-config-v1.schema.json +244 -0
  90. package/schemas/qualification-invocation-request-v1.schema.json +59 -0
  91. package/schemas/qualification-judge-panel-result-v1.schema.json +42 -0
  92. package/schemas/results-v2.schema.json +19 -21
  93. package/schemas/results-v3.schema.json +654 -0
  94. package/schemas/specification-v1.schema.json +1 -1
  95. package/schemas/trajectory-event-v1.schema.json +20 -1
@@ -0,0 +1,63 @@
1
+ export interface CalibrationComponent {
2
+ kind: "detector" | "adjudicator";
3
+ id: string;
4
+ version: string;
5
+ population: string;
6
+ }
7
+ export type CalibrationKind = "positive" | "approval" | "rejection";
8
+ export type CalibrationSplit = "calibration" | "heldout" | "tuning";
9
+ export interface CalibrationPrediction {
10
+ id: string;
11
+ incidentId: string;
12
+ component: CalibrationComponent;
13
+ kind: CalibrationKind;
14
+ split: CalibrationSplit;
15
+ }
16
+ /** Independently established host context, not fields accepted from predictions or panel agreement. */
17
+ export interface CalibrationOutcome extends CalibrationPrediction {
18
+ correct: boolean;
19
+ }
20
+ export interface CalibrationReport {
21
+ component: CalibrationComponent;
22
+ kind: CalibrationKind;
23
+ split: Exclude<CalibrationSplit, "tuning">;
24
+ correct: number;
25
+ incorrect: number;
26
+ resolved: number;
27
+ unresolved: number;
28
+ conflicted: number;
29
+ totalIncidents: number;
30
+ precision: number | null;
31
+ interval: {
32
+ lower: number;
33
+ upper: number;
34
+ } | null;
35
+ advisoryOnly: true;
36
+ }
37
+ /** Version/population/conditioning-specific statistics; no policy, grants, routing or adoption is changed. */
38
+ export declare function calibratePredictions(predictions: readonly CalibrationPrediction[], context?: {
39
+ outcomes: readonly CalibrationOutcome[];
40
+ }): {
41
+ version: "factory-calibration-v1";
42
+ reports: CalibrationReport[];
43
+ excludedTuning: number;
44
+ referenceBasis: "independently-supplied-host-context";
45
+ advisoryOnly: true;
46
+ };
47
+ export interface ExposurePolicy {
48
+ id: string;
49
+ component: CalibrationComponent;
50
+ kind: CalibrationKind;
51
+ split: "calibration" | "heldout";
52
+ minimumResolved: number;
53
+ minimumLowerBound: number;
54
+ attentionRemaining: number;
55
+ expiresAt: number;
56
+ retireBelowUpperBound?: number;
57
+ }
58
+ /** A selected pre-established host policy may produce advice; this never applies it or expands authority. */
59
+ export declare function recommendExposure(report: CalibrationReport, policy: ExposurePolicy | null, now: number): {
60
+ mode: "silent" | "ask" | "retire";
61
+ reason: string;
62
+ advisoryOnly: true;
63
+ };
@@ -0,0 +1,115 @@
1
+ const computedReports = new WeakSet();
2
+ const text = (x) => typeof x === "string" && x.length > 0 && x.length <= 512 && !/[\u0000-\u001f\u007f]/.test(x);
3
+ function closed(x, fields) {
4
+ return x !== null && typeof x === "object" && !Array.isArray(x) && Object.keys(x).length === fields.length && Object.keys(x).every(k => fields.includes(k));
5
+ }
6
+ function valid(x, outcome = false) {
7
+ return closed(x, outcome ? ["id", "incidentId", "component", "kind", "split", "correct"] : ["id", "incidentId", "component", "kind", "split"])
8
+ && text(x.id) && text(x.incidentId) && closed(x.component, ["kind", "id", "version", "population"])
9
+ && [x.component.id, x.component.version, x.component.population].every(text)
10
+ && ["calibration", "heldout", "tuning"].includes(x.split)
11
+ && (x.component.kind === "detector" ? x.kind === "positive" : x.component.kind === "adjudicator" && ["approval", "rejection"].includes(x.kind))
12
+ && (!outcome || typeof x.correct === "boolean");
13
+ }
14
+ const key = (x) => JSON.stringify([x.component.kind, x.component.id, x.component.version, x.component.population, x.kind, x.split]);
15
+ function interval(correct, count) {
16
+ const z = 1.96, z2 = z * z, p = correct / count, denominator = 1 + z2 / count;
17
+ const center = (p + z2 / (2 * count)) / denominator;
18
+ const half = z * Math.sqrt(p * (1 - p) / count + z2 / (4 * count * count)) / denominator;
19
+ return { lower: Math.max(0, center - half), upper: Math.min(1, center + half) };
20
+ }
21
+ /** Version/population/conditioning-specific statistics; no policy, grants, routing or adoption is changed. */
22
+ export function calibratePredictions(predictions, context = { outcomes: [] }) {
23
+ if (predictions.length > 10000 || context.outcomes.length > 10000 || predictions.some(p => !valid(p)) || context.outcomes.some(r => !valid(r, true)))
24
+ throw new Error("invalid calibration input");
25
+ const byId = new Map();
26
+ for (const p of predictions) {
27
+ const previous = byId.get(p.id);
28
+ if (previous && (key(previous) !== key(p) || previous.incidentId !== p.incidentId))
29
+ throw new Error("prediction identity conflict");
30
+ byId.set(p.id, p);
31
+ }
32
+ const groups = new Map();
33
+ let excludedTuning = 0;
34
+ for (const p of byId.values()) {
35
+ if (p.split === "tuning") {
36
+ excludedTuning++;
37
+ continue;
38
+ }
39
+ const k = key(p);
40
+ groups.set(k, [...(groups.get(k) ?? []), p]);
41
+ }
42
+ const referenceIds = new Map();
43
+ const conflictingIds = new Set();
44
+ for (const r of context.outcomes) {
45
+ const signature = JSON.stringify([key(r), r.incidentId, r.correct]);
46
+ if (referenceIds.has(r.id) && referenceIds.get(r.id) !== signature)
47
+ conflictingIds.add(r.id);
48
+ referenceIds.set(r.id, signature);
49
+ }
50
+ const labels = new Map();
51
+ const conflictedIncidents = new Set();
52
+ for (const r of context.outcomes) {
53
+ const k = JSON.stringify([key(r), r.incidentId]);
54
+ if (conflictingIds.has(r.id)) {
55
+ conflictedIncidents.add(k);
56
+ continue;
57
+ }
58
+ const values = labels.get(k) ?? new Set();
59
+ values.add(r.correct);
60
+ labels.set(k, values);
61
+ }
62
+ const reports = [...groups].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([groupKey, group]) => {
63
+ const incidents = new Set(group.map(p => p.incidentId));
64
+ let correct = 0, incorrect = 0, unresolved = 0, conflicted = 0;
65
+ for (const incident of incidents) {
66
+ const incidentKey = JSON.stringify([groupKey, incident]);
67
+ const outcomes = labels.get(incidentKey) ?? new Set();
68
+ if (outcomes.size !== 1 || conflictedIncidents.has(incidentKey)) {
69
+ unresolved++;
70
+ if (outcomes.size > 1 || conflictedIncidents.has(incidentKey))
71
+ conflicted++;
72
+ }
73
+ else if (outcomes.has(true))
74
+ correct++;
75
+ else
76
+ incorrect++;
77
+ }
78
+ const resolved = correct + incorrect;
79
+ return { component: { ...group[0].component }, kind: group[0].kind, split: group[0].split,
80
+ correct, incorrect, resolved, unresolved, conflicted, totalIncidents: incidents.size,
81
+ precision: resolved ? correct / resolved : null, interval: resolved ? interval(correct, resolved) : null, advisoryOnly: true };
82
+ });
83
+ for (const report of reports) {
84
+ Object.freeze(report.component);
85
+ if (report.interval)
86
+ Object.freeze(report.interval);
87
+ Object.freeze(report);
88
+ computedReports.add(report);
89
+ }
90
+ return { version: "factory-calibration-v1", reports, excludedTuning, referenceBasis: "independently-supplied-host-context", advisoryOnly: true };
91
+ }
92
+ /** A selected pre-established host policy may produce advice; this never applies it or expands authority. */
93
+ export function recommendExposure(report, policy, now) {
94
+ if (!computedReports.has(report))
95
+ throw new Error("recompute calibration from predictions and independent reference context before evaluating exposure");
96
+ const result = (mode, reason) => ({ mode, reason, advisoryOnly: true });
97
+ if (!policy)
98
+ return result("silent", "policy-unavailable");
99
+ if (!text(policy.id) || !Number.isSafeInteger(policy.minimumResolved) || policy.minimumResolved < 1
100
+ || !Number.isSafeInteger(policy.attentionRemaining) || policy.attentionRemaining < 0
101
+ || !Number.isFinite(policy.minimumLowerBound) || policy.minimumLowerBound < 0 || policy.minimumLowerBound > 1
102
+ || !Number.isFinite(policy.expiresAt) || !Number.isFinite(now)
103
+ || (policy.retireBelowUpperBound !== undefined && (!Number.isFinite(policy.retireBelowUpperBound) || policy.retireBelowUpperBound < 0 || policy.retireBelowUpperBound > 1)))
104
+ throw new Error("invalid exposure policy");
105
+ if (key(policy) !== key(report) || now >= policy.expiresAt)
106
+ return result("silent", "policy-inapplicable-or-expired");
107
+ if (!report.interval || report.resolved < policy.minimumResolved)
108
+ return result("silent", "insufficient-independent-evidence");
109
+ if (policy.retireBelowUpperBound !== undefined && report.interval.upper < policy.retireBelowUpperBound)
110
+ return result("retire", "below-selected-reliability-policy");
111
+ if (report.interval.lower < policy.minimumLowerBound || policy.attentionRemaining === 0)
112
+ return result("silent", "confidence-or-attention-bound");
113
+ return result("ask", "selected-evidence-and-attention-policy-met");
114
+ }
115
+ //# sourceMappingURL=factory-calibration.js.map
package/dist/grade.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { Scenario } from "./spec.js";
2
2
  import type { HarnessAdapter, ModelRef } from "./adapters/types.js";
3
3
  import type { Verdict } from "./score.js";
4
+ import { type CriterionVote } from "./results.js";
4
5
  export interface JudgePromptInput {
5
6
  skill: string;
6
7
  persona: string;
@@ -36,6 +37,7 @@ export declare function parseVerdict(out: string): ParsedVerdict;
36
37
  export declare function judgeResemblesSubject(judge: ModelRef, subject: ModelRef): boolean;
37
38
  export interface GradeResult extends ParsedVerdict {
38
39
  raw: string;
40
+ criteria: CriterionVote[];
39
41
  /** Judge misfire: the overall verdict disagrees with AND(per-item grades). Recorded, never auto-passed; blocks SHIP until re-judged or overridden. */
40
42
  suspect: boolean;
41
43
  }
package/dist/grade.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { createWorkspace } from "./workspace.js";
2
+ import { parseCriterionVotes } from "./results.js";
2
3
  /** The heading runSeeded writes above the staged diff. Gating on this exact string is what keeps the guidance honest — see below. */
3
4
  export const STAGED_DIFF_HEADING = "=== STAGED DIFF ===";
4
5
  /**
@@ -133,7 +134,7 @@ export async function gradeTranscript(adapter, judge, prompt, cwd) {
133
134
  parsed.reason = `judge unparseable: ${snippet}`;
134
135
  }
135
136
  const suspect = detectMisfire(raw, parsed.verdict);
136
- return { ...parsed, raw, suspect };
137
+ return { ...parsed, raw, suspect, criteria: parseCriterionVotes(raw) };
137
138
  }
138
139
  /**
139
140
  * Grade a transcript in a fresh, isolated, throwaway workspace — never the
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./trusted-host-supervision.js";
1
2
  export * from "./spec.js";
2
3
  export * from "./discover.js";
3
4
  export * from "./run.js";
@@ -23,12 +24,24 @@ export * from "./util/env.js";
23
24
  export * from "./scaffold.js";
24
25
  export * from "./version.js";
25
26
  export * from "./defaults.js";
27
+ export * from "./command-cost.js";
26
28
  export * from "./judge-policy.js";
29
+ export * from "./judge-agreement.js";
27
30
  export * from "./regate.js";
31
+ export * from "./screen.js";
32
+ export * from "./prompt-normalization.js";
28
33
  export * from "./downgrade.js";
29
34
  export * from "./canary.js";
30
35
  export * from "./stability.js";
31
36
  export * from "./capture-trace-types.js";
37
+ export * from "./work-capture.js";
38
+ export * from "./work-signals.js";
39
+ export * from "./work-capture-schema.js";
40
+ export * from "./factory-calibration.js";
41
+ export * from "./adoption.js";
42
+ export * from "./investigation.js";
43
+ export * from "./intervention.js";
44
+ export * from "./intervention-results.js";
32
45
  export * from "./spec-write.js";
33
46
  export * from "./capture.js";
34
47
  export * from "./execution-trace.js";
@@ -37,7 +50,15 @@ export * from "./trajectory-gates.js";
37
50
  export * from "./instruction-coverage.js";
38
51
  export * from "./affected.js";
39
52
  export * from "./adjudication.js";
53
+ export * from "./vote-panel.js";
54
+ export * from "./qualification-panels.js";
55
+ export * from "./qualification-panel-store.js";
56
+ export * from "./qualification-validation.js";
40
57
  export * from "./comparison.js";
41
58
  export * from "./sandbox.js";
42
59
  export * from "./provider-failure.js";
43
60
  export * from "./arms.js";
61
+ export * from "./qualification-config.js";
62
+ export * from "./qualification-oauth-directory.js";
63
+ export * from "./qualification-store.js";
64
+ export * from "./qualification-runner.js";
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./trusted-host-supervision.js";
1
2
  export * from "./spec.js";
2
3
  export * from "./discover.js";
3
4
  export * from "./run.js";
@@ -23,12 +24,24 @@ export * from "./util/env.js";
23
24
  export * from "./scaffold.js";
24
25
  export * from "./version.js";
25
26
  export * from "./defaults.js";
27
+ export * from "./command-cost.js";
26
28
  export * from "./judge-policy.js";
29
+ export * from "./judge-agreement.js";
27
30
  export * from "./regate.js";
31
+ export * from "./screen.js";
32
+ export * from "./prompt-normalization.js";
28
33
  export * from "./downgrade.js";
29
34
  export * from "./canary.js";
30
35
  export * from "./stability.js";
31
36
  export * from "./capture-trace-types.js";
37
+ export * from "./work-capture.js";
38
+ export * from "./work-signals.js";
39
+ export * from "./work-capture-schema.js";
40
+ export * from "./factory-calibration.js";
41
+ export * from "./adoption.js";
42
+ export * from "./investigation.js";
43
+ export * from "./intervention.js";
44
+ export * from "./intervention-results.js";
32
45
  export * from "./spec-write.js";
33
46
  export * from "./capture.js";
34
47
  export * from "./execution-trace.js";
@@ -37,8 +50,16 @@ export * from "./trajectory-gates.js";
37
50
  export * from "./instruction-coverage.js";
38
51
  export * from "./affected.js";
39
52
  export * from "./adjudication.js";
53
+ export * from "./vote-panel.js";
54
+ export * from "./qualification-panels.js";
55
+ export * from "./qualification-panel-store.js";
56
+ export * from "./qualification-validation.js";
40
57
  export * from "./comparison.js";
41
58
  export * from "./sandbox.js";
42
59
  export * from "./provider-failure.js";
43
60
  export * from "./arms.js";
61
+ export * from "./qualification-config.js";
62
+ export * from "./qualification-oauth-directory.js";
63
+ export * from "./qualification-store.js";
64
+ export * from "./qualification-runner.js";
44
65
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,6 @@
1
+ import type { InterventionManifest, InterventionEvidence } from "./intervention.js";
2
+ /** Normalize existing validated schema3 records, retaining the complete source votes alongside the summary. */
3
+ export declare function interventionEvidenceFromResults(manifest: InterventionManifest, armId: string, raw: unknown, outputBytes: ReadonlyMap<string, Uint8Array>): {
4
+ evidence: InterventionEvidence;
5
+ sourceResults: import("./results.js").ResultsFile;
6
+ };
@@ -0,0 +1,52 @@
1
+ import { createHash } from "node:crypto";
2
+ import { validateResults } from "./results.js";
3
+ import { aggregateMetrics } from "./comparison.js";
4
+ /** Normalize existing validated schema3 records, retaining the complete source votes alongside the summary. */
5
+ export function interventionEvidenceFromResults(manifest, armId, raw, outputBytes) {
6
+ const sourceResults = validateResults(raw), arm = manifest.arms.find(a => a.id === armId);
7
+ if (!arm || sourceResults.model !== arm.configuration.model)
8
+ throw new Error("intervention result model mismatch");
9
+ if (sourceResults.schema !== 3 || sourceResults.partial || sourceResults.mode !== manifest.common.mode
10
+ || `${sourceResults.judge.provider}:${sourceResults.judge.model}` !== manifest.judge)
11
+ throw new Error("incomplete or incompatible intervention results");
12
+ if (sourceResults.source_hashes?.["intervention:manifest"] !== manifest.id || sourceResults.source_hashes?.["intervention:inputs"] !== manifest.inputDigest)
13
+ throw new Error("recorded frozen intervention binding missing; never stamp historical results");
14
+ if (sourceResults.scenarios.length !== manifest.cases.length || sourceResults.scenarios.some(s => !manifest.cases.some(c => c.id === s.id)))
15
+ throw new Error("intervention scenario plan mismatch");
16
+ const cells = [], artifacts = new Set();
17
+ for (const spec of manifest.cases) {
18
+ const scenario = sourceResults.scenarios.find(s => s.id === spec.id);
19
+ if (scenario.criterion_count !== spec.criteria || (scenario.reps ?? 1) !== spec.reps || (scenario.pass_threshold ?? 1) !== spec.threshold)
20
+ throw new Error("intervention repetition/criterion policy mismatch");
21
+ for (const panel of scenario.rep_judgments ?? []) {
22
+ const delivery = sourceResults.subject_invocations.find(o => o.scenario_id === spec.id && o.repetition === panel.repetition)?.prompt?.status;
23
+ if (!delivery || !panel.objective)
24
+ throw new Error("intervention objective/delivery evidence missing");
25
+ const objective = panel.objective.status;
26
+ const bytes = outputBytes.get(`${spec.id}:${panel.repetition}`);
27
+ if (bytes !== undefined && (!(bytes instanceof Uint8Array) || bytes.byteLength > 8 * 1024 * 1024))
28
+ throw new Error("intervention output exceeds bounds");
29
+ const artifactSha256 = bytes ? createHash("sha256").update(bytes).digest("hex") : null;
30
+ if (artifactSha256)
31
+ artifacts.add(artifactSha256);
32
+ let criteria = [], suspect = false;
33
+ if (delivery === "PASS" && objective === "PASS") {
34
+ if (panel.judgments.length !== 1)
35
+ throw new Error("multi-judge normalization not yet supported; retain original panel without selecting a convenient vote");
36
+ const judgment = panel.judgments[0];
37
+ if (`${judgment.judge.provider}:${judgment.judge.model}` !== manifest.judge)
38
+ throw new Error("intervention cell judge mismatch");
39
+ criteria = [...(judgment.criteria ?? [])].sort((a, b) => a.index - b.index).map(c => c.verdict === "ERROR" ? "UNKNOWN" : c.verdict);
40
+ suspect = judgment.suspect || scenario.suspect;
41
+ }
42
+ cells.push({ caseId: spec.id, repetition: panel.repetition, delivery, objective, criteria, suspect, artifactSha256 });
43
+ }
44
+ }
45
+ const metrics = aggregateMetrics(sourceResults.scenarios);
46
+ const cost = manifest.resourceMetric === "wall_ms" ? (sourceResults.scenarios.every(s => s.metrics) ? metrics.wall_time_ms : null)
47
+ : manifest.resourceMetric === "tool_calls" ? metrics.tool_calls
48
+ : metrics.cost_source === "provider-reported" && metrics.subject_metrics_reps === metrics.total_reps ? metrics.subject_cost_usd : null;
49
+ const evidence = { armId, inputDigest: manifest.inputDigest, artifactDigests: [...artifacts].sort(), cells, cost, costUnit: manifest.resourceMetric };
50
+ return { evidence, sourceResults };
51
+ }
52
+ //# sourceMappingURL=intervention-results.js.map
@@ -0,0 +1,123 @@
1
+ /** Plain, bounded JSON encoding for retained intervention inputs; not semantic validation. */
2
+ export declare function interventionCanonicalJson(value: unknown): string;
3
+ export type InterventionAxis = "model" | "effort" | "skill" | "prompt" | "configuration";
4
+ export interface InterventionDraft {
5
+ family: "intervention";
6
+ investigationSha256: string;
7
+ resourceMetric: "usd" | "wall_ms" | "tool_calls";
8
+ axes: InterventionAxis[];
9
+ common: {
10
+ mode: "green" | "force";
11
+ scenarioSha256: string;
12
+ rubricSha256: string;
13
+ fixtureSha256: string;
14
+ heldoutSha256: string;
15
+ harnessSha256: string;
16
+ judgePolicySha256: string;
17
+ };
18
+ proposer: string;
19
+ judge: string;
20
+ cases: Array<{
21
+ id: string;
22
+ criteria: number;
23
+ reps: number;
24
+ threshold: number;
25
+ critical: boolean;
26
+ }>;
27
+ arms: Array<{
28
+ id: string;
29
+ configuration: Record<InterventionAxis, string>;
30
+ }>;
31
+ }
32
+ export interface InterventionManifest extends InterventionDraft {
33
+ version: "intervention-comparison-v1";
34
+ id: string;
35
+ inputDigest: string;
36
+ changedAxes: InterventionAxis[];
37
+ deterministicSampling: false;
38
+ }
39
+ export declare function freezeIntervention(input: InterventionDraft): InterventionManifest;
40
+ export interface InterventionCell {
41
+ caseId: string;
42
+ repetition: number;
43
+ delivery: "PASS" | "NOT-MEASURED" | "ERROR";
44
+ objective: "PASS" | "FAIL" | "ERROR" | "NOT-MEASURED";
45
+ criteria: Array<"PASS" | "FAIL" | "UNKNOWN">;
46
+ suspect: boolean;
47
+ artifactSha256: string | null;
48
+ }
49
+ export interface InterventionEvidence {
50
+ armId: string;
51
+ inputDigest: string;
52
+ artifactDigests: string[];
53
+ cells: InterventionCell[];
54
+ cost: number | null;
55
+ costUnit: string;
56
+ }
57
+ export declare const interventionEvidenceDigest: (value: InterventionEvidence) => string;
58
+ export interface InterventionQualification {
59
+ manifestId: string;
60
+ proposer: {
61
+ requested: string;
62
+ canonical: string;
63
+ };
64
+ judge: {
65
+ requested: string;
66
+ canonical: string;
67
+ };
68
+ subjects: Record<string, {
69
+ requested: string;
70
+ canonical: string;
71
+ }>;
72
+ evidenceDigests: Record<string, string>;
73
+ /** Actual independently retained output bytes, not an availability flag supplied by an arm. */
74
+ artifacts: ReadonlyMap<string, Uint8Array>;
75
+ }
76
+ export interface InterventionAssessment {
77
+ manifestId: string;
78
+ complete: boolean;
79
+ arms: Array<{
80
+ armId: string;
81
+ state: "MEASURED" | "FAILED" | "MISSING" | "ERROR" | "NOT-MEASURED" | "UNRESOLVED";
82
+ eligible: boolean;
83
+ artifactDigests: string[];
84
+ cost: number | null;
85
+ costUnit: string;
86
+ }>;
87
+ cheapestEligible: string | null;
88
+ routingDefault: null;
89
+ adoptionAuthorized: false;
90
+ }
91
+ /** Pre-spend role gate: consumes independently resolved host facts, never guesses aliases or calls a provider. */
92
+ export declare function assertInterventionRoles(manifest: InterventionManifest, qualification: InterventionQualification | null): asserts qualification is InterventionQualification;
93
+ /** Inspects separately host-qualified evidence; does not obtain qualification or execute a model. */
94
+ export declare function assessIntervention(manifest: InterventionManifest, evidence: readonly InterventionEvidence[], qualification: InterventionQualification | null): InterventionAssessment;
95
+ export type BlindQualityChoice = {
96
+ kind: "one" | "tie" | "none" | "insufficient";
97
+ labels: string[];
98
+ };
99
+ /** Only this view omits identity/cost; arbitrary artifact content can still leak clues and must be reviewed. */
100
+ export declare function createBlindComparison(manifest: InterventionManifest, assessment: InterventionAssessment, fixtureOrPersistedSeed?: string): Readonly<{
101
+ view: () => {
102
+ version: string;
103
+ cards: {
104
+ label: string;
105
+ artifactDigests: string[];
106
+ }[];
107
+ limitations: string[];
108
+ };
109
+ readArtifact(label: string, hash: string): Buffer<ArrayBuffer>;
110
+ choose(input: BlindQualityChoice): BlindQualityChoice;
111
+ reveal(): {
112
+ manifestId: string;
113
+ choice: BlindQualityChoice;
114
+ arms: {
115
+ label: string;
116
+ armId: string;
117
+ configuration: Record<InterventionAxis, string>;
118
+ cost: number | null;
119
+ costUnit: string;
120
+ }[];
121
+ routingDefault: null;
122
+ };
123
+ }>;