@mutagent/evaluator 0.1.0-alpha.2

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 (126) hide show
  1. package/.claude/skills/mutagent-evaluator/SKILL.md +538 -0
  2. package/.claude/skills/mutagent-evaluator/assets/agents/audit-executor.md +169 -0
  3. package/.claude/skills/mutagent-evaluator/assets/agents/dataset-builder.md +160 -0
  4. package/.claude/skills/mutagent-evaluator/assets/agents/discovery.md +425 -0
  5. package/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +1044 -0
  6. package/.claude/skills/mutagent-evaluator/assets/brand/theme.css +213 -0
  7. package/.claude/skills/mutagent-evaluator/assets/brand/wordmark.html +9 -0
  8. package/.claude/skills/mutagent-evaluator/lenses/context-flow-lens.md +85 -0
  9. package/.claude/skills/mutagent-evaluator/lenses/data-lens.md +47 -0
  10. package/.claude/skills/mutagent-evaluator/lenses/decision-lens.md +48 -0
  11. package/.claude/skills/mutagent-evaluator/lenses/methodology-critic-lens.md +53 -0
  12. package/.claude/skills/mutagent-evaluator/lenses/trajectory-lens.md +44 -0
  13. package/.claude/skills/mutagent-evaluator/references/build-review-interface.md +83 -0
  14. package/.claude/skills/mutagent-evaluator/references/edd-loop.md +134 -0
  15. package/.claude/skills/mutagent-evaluator/references/error-analysis.md +113 -0
  16. package/.claude/skills/mutagent-evaluator/references/eval-audit.md +154 -0
  17. package/.claude/skills/mutagent-evaluator/references/eval-stage.md +168 -0
  18. package/.claude/skills/mutagent-evaluator/references/generate-synthetic-data.md +81 -0
  19. package/.claude/skills/mutagent-evaluator/references/grounded-adjudication.md +221 -0
  20. package/.claude/skills/mutagent-evaluator/references/memory-format.md +65 -0
  21. package/.claude/skills/mutagent-evaluator/references/methodology.md +201 -0
  22. package/.claude/skills/mutagent-evaluator/references/operation-inventory.md +196 -0
  23. package/.claude/skills/mutagent-evaluator/references/validate-evaluator.md +125 -0
  24. package/.claude/skills/mutagent-evaluator/references/workflows/orchestrator-protocol.md +287 -0
  25. package/.claude/skills/mutagent-evaluator/references/write-judge-prompt.md +123 -0
  26. package/.claude/skills/mutagent-evaluator/schemas/behavior-tree.schema.yaml +73 -0
  27. package/.claude/skills/mutagent-evaluator/schemas/dataset.schema.yaml +66 -0
  28. package/.claude/skills/mutagent-evaluator/schemas/edd-change-request.schema.yaml +114 -0
  29. package/.claude/skills/mutagent-evaluator/schemas/eval-matrix.schema.yaml +74 -0
  30. package/.claude/skills/mutagent-evaluator/schemas/flow-graph.schema.yaml +69 -0
  31. package/.claude/skills/mutagent-evaluator/schemas/flow-profile.schema.yaml +49 -0
  32. package/.claude/skills/mutagent-evaluator/schemas/methodology-review.schema.yaml +40 -0
  33. package/.claude/skills/mutagent-evaluator/schemas/scorecard.schema.yaml +85 -0
  34. package/.claude/skills/mutagent-evaluator/scripts/agent-dispatch.ts +0 -0
  35. package/.claude/skills/mutagent-evaluator/scripts/aggregate-discover.ts +543 -0
  36. package/.claude/skills/mutagent-evaluator/scripts/artifact-paths.ts +99 -0
  37. package/.claude/skills/mutagent-evaluator/scripts/assemble-scorecard.ts +172 -0
  38. package/.claude/skills/mutagent-evaluator/scripts/build-dataset.ts +186 -0
  39. package/.claude/skills/mutagent-evaluator/scripts/build-evals.ts +93 -0
  40. package/.claude/skills/mutagent-evaluator/scripts/build-review-ui.ts +393 -0
  41. package/.claude/skills/mutagent-evaluator/scripts/check-method-router.ts +170 -0
  42. package/.claude/skills/mutagent-evaluator/scripts/cli/aggregate.ts +112 -0
  43. package/.claude/skills/mutagent-evaluator/scripts/cli/audit-run.ts +175 -0
  44. package/.claude/skills/mutagent-evaluator/scripts/cli/doctor.ts +211 -0
  45. package/.claude/skills/mutagent-evaluator/scripts/cli/dogfood.ts +133 -0
  46. package/.claude/skills/mutagent-evaluator/scripts/cli/init.ts +601 -0
  47. package/.claude/skills/mutagent-evaluator/scripts/cli/methodology-review.ts +122 -0
  48. package/.claude/skills/mutagent-evaluator/scripts/cli/prep.ts +279 -0
  49. package/.claude/skills/mutagent-evaluator/scripts/cli/profile-subject.ts +165 -0
  50. package/.claude/skills/mutagent-evaluator/scripts/cli/run.sh +56 -0
  51. package/.claude/skills/mutagent-evaluator/scripts/cli/variance-check.ts +105 -0
  52. package/.claude/skills/mutagent-evaluator/scripts/code-eval.ts +248 -0
  53. package/.claude/skills/mutagent-evaluator/scripts/codegen-evals.ts +173 -0
  54. package/.claude/skills/mutagent-evaluator/scripts/cold-start-project.ts +151 -0
  55. package/.claude/skills/mutagent-evaluator/scripts/cold-start-sampler.ts +267 -0
  56. package/.claude/skills/mutagent-evaluator/scripts/config/load.ts +307 -0
  57. package/.claude/skills/mutagent-evaluator/scripts/config/schema.ts +325 -0
  58. package/.claude/skills/mutagent-evaluator/scripts/contracts/agentspec-evals.ts +85 -0
  59. package/.claude/skills/mutagent-evaluator/scripts/contracts/dataset.ts +149 -0
  60. package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-engine.ts +118 -0
  61. package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +577 -0
  62. package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-types.ts +1074 -0
  63. package/.claude/skills/mutagent-evaluator/scripts/contracts/flow-graph.ts +194 -0
  64. package/.claude/skills/mutagent-evaluator/scripts/contracts/types.ts +303 -0
  65. package/.claude/skills/mutagent-evaluator/scripts/contracts/validation.ts +193 -0
  66. package/.claude/skills/mutagent-evaluator/scripts/derive-dataset.ts +152 -0
  67. package/.claude/skills/mutagent-evaluator/scripts/determine-outcome.ts +219 -0
  68. package/.claude/skills/mutagent-evaluator/scripts/diff-discriminate.ts +160 -0
  69. package/.claude/skills/mutagent-evaluator/scripts/discover-criteria.ts +348 -0
  70. package/.claude/skills/mutagent-evaluator/scripts/edd/change-request.ts +232 -0
  71. package/.claude/skills/mutagent-evaluator/scripts/edd/edd-types.ts +210 -0
  72. package/.claude/skills/mutagent-evaluator/scripts/edd/variance-gate.ts +186 -0
  73. package/.claude/skills/mutagent-evaluator/scripts/emit-completeness.ts +111 -0
  74. package/.claude/skills/mutagent-evaluator/scripts/eval-engine.ts +160 -0
  75. package/.claude/skills/mutagent-evaluator/scripts/evaluate.ts +333 -0
  76. package/.claude/skills/mutagent-evaluator/scripts/flow-graph.ts +230 -0
  77. package/.claude/skills/mutagent-evaluator/scripts/judge-prompt-template.ts +253 -0
  78. package/.claude/skills/mutagent-evaluator/scripts/judge-provider.ts +135 -0
  79. package/.claude/skills/mutagent-evaluator/scripts/lint-grounding.ts +229 -0
  80. package/.claude/skills/mutagent-evaluator/scripts/lint-uniformity.ts +168 -0
  81. package/.claude/skills/mutagent-evaluator/scripts/living-suite.ts +109 -0
  82. package/.claude/skills/mutagent-evaluator/scripts/load-bundle.ts +203 -0
  83. package/.claude/skills/mutagent-evaluator/scripts/load-profile-vocab.ts +64 -0
  84. package/.claude/skills/mutagent-evaluator/scripts/load-profile.ts +106 -0
  85. package/.claude/skills/mutagent-evaluator/scripts/mask.ts +138 -0
  86. package/.claude/skills/mutagent-evaluator/scripts/materialize-dataset.ts +113 -0
  87. package/.claude/skills/mutagent-evaluator/scripts/matrix-judge.ts +750 -0
  88. package/.claude/skills/mutagent-evaluator/scripts/memory/append.ts +215 -0
  89. package/.claude/skills/mutagent-evaluator/scripts/memory/read.ts +168 -0
  90. package/.claude/skills/mutagent-evaluator/scripts/prep-tasks.ts +125 -0
  91. package/.claude/skills/mutagent-evaluator/scripts/profile-subject.ts +310 -0
  92. package/.claude/skills/mutagent-evaluator/scripts/publish-report.ts +131 -0
  93. package/.claude/skills/mutagent-evaluator/scripts/read-unitf-traces.ts +81 -0
  94. package/.claude/skills/mutagent-evaluator/scripts/render-build-cards.ts +195 -0
  95. package/.claude/skills/mutagent-evaluator/scripts/render-discover-report.ts +1640 -0
  96. package/.claude/skills/mutagent-evaluator/scripts/render-eval-report.ts +3823 -0
  97. package/.claude/skills/mutagent-evaluator/scripts/render-report.ts +212 -0
  98. package/.claude/skills/mutagent-evaluator/scripts/resolve-credential.ts +110 -0
  99. package/.claude/skills/mutagent-evaluator/scripts/resolve-ref.ts +98 -0
  100. package/.claude/skills/mutagent-evaluator/scripts/result-verify.ts +129 -0
  101. package/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +320 -0
  102. package/.claude/skills/mutagent-evaluator/scripts/run-deterministic.ts +271 -0
  103. package/.claude/skills/mutagent-evaluator/scripts/run-evaluate.ts +715 -0
  104. package/.claude/skills/mutagent-evaluator/scripts/run-judge.ts +155 -0
  105. package/.claude/skills/mutagent-evaluator/scripts/run-pipeline.ts +175 -0
  106. package/.claude/skills/mutagent-evaluator/scripts/sample-traces.ts +210 -0
  107. package/.claude/skills/mutagent-evaluator/scripts/self-audit.ts +387 -0
  108. package/.claude/skills/mutagent-evaluator/scripts/source-map.ts +106 -0
  109. package/.claude/skills/mutagent-evaluator/scripts/subject-profile.ts +134 -0
  110. package/.claude/skills/mutagent-evaluator/scripts/substrate.ts +162 -0
  111. package/.claude/skills/mutagent-evaluator/scripts/ui-slots.ts +119 -0
  112. package/.claude/skills/mutagent-evaluator/scripts/unitf-to-evaltrace.ts +284 -0
  113. package/.claude/skills/mutagent-evaluator/scripts/validate-judge.ts +358 -0
  114. package/.claude/skills/mutagent-evaluator/scripts/variance-compare.ts +177 -0
  115. package/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/behavior-tree.yaml +140 -0
  116. package/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/eval-matrix.yaml +1270 -0
  117. package/.claude/skills/mutagent-evaluator/subjects/mutagent-diagnostics/methodology-review.yaml +105 -0
  118. package/.claude/skills/mutagent-evaluator/workflows/audit.workflow.js +82 -0
  119. package/.claude/skills/mutagent-evaluator/workflows/data-leak.workflow.js +236 -0
  120. package/.claude/skills/mutagent-evaluator/workflows/variance.workflow.js +163 -0
  121. package/LICENSE +201 -0
  122. package/NOTICE +23 -0
  123. package/README.md +90 -0
  124. package/bin/mutagent-cli.mjs +9263 -0
  125. package/bin/mutagent-evaluator.mjs +96 -0
  126. package/package.json +52 -0
@@ -0,0 +1,320 @@
1
+ /**
2
+ * scripts/route-failures.ts — EV-051 route-failures (judge-only handoff).
3
+ * ---------------------------------------------------------------------------
4
+ * "A judge is only a judge." The evaluator FLAGS failures and ROUTES them to
5
+ * diagnostics — it NEVER fixes. This emits a HandoverBundle for the diagnose
6
+ * stage: the failing criteria become the acceptance criteria the downstream
7
+ * diagnose stage must root-cause, and the failing traces/scorecard become the
8
+ * enumerated `inputs` that cross the boundary.
9
+ *
10
+ * Sealed-sibling: the HandoverBundle shape is RE-IMPLEMENTED here (mirroring
11
+ * the orchestrator's handover-contract.ts SHAPE), NEVER imported across the
12
+ * package boundary. This is a one-way EMIT (the evaluator produces it; some
13
+ * diagnose stage consumes it), not a shared frozen contract co-owned by both
14
+ * packages — so it stays in-package rather than escalating to templates/.
15
+ *
16
+ * PURE + deterministic: provenance (produced_by/at) is INJECTED, never a
17
+ * self-read clock, so the same input yields a byte-identical bundle. The
18
+ * bundle is a CLOSED object (additionalProperties:false at every level) so a
19
+ * smuggled undeclared field is caught by validateHandoverBundle.
20
+ */
21
+ import { type Static, Type } from "@sinclair/typebox";
22
+ import { TypeCompiler } from "@sinclair/typebox/compiler";
23
+ import {
24
+ OutcomeVerdict,
25
+ type CriterionFlagValue,
26
+ type OutcomeVerdictValue,
27
+ type VerdictBlock,
28
+ } from "./contracts/eval-types.ts";
29
+ import { maskedCanonicalJson } from "./mask.ts";
30
+
31
+ /** FROZEN contract version (in-package mirror; bump only via reviewed migration). */
32
+ export const HANDOVER_BUNDLE_VERSION = "0.1.0" as const;
33
+
34
+ export const AdlStage = {
35
+ Build: "build",
36
+ Evaluate: "evaluate",
37
+ Diagnose: "diagnose",
38
+ Improve: "improve",
39
+ Audit: "audit",
40
+ } as const;
41
+ export type AdlStageValue = (typeof AdlStage)[keyof typeof AdlStage];
42
+
43
+ export const SubjectKind = { Skill: "skill", Agent: "agent" } as const;
44
+ export type SubjectKindValue = (typeof SubjectKind)[keyof typeof SubjectKind];
45
+
46
+ export const ArtifactKind = {
47
+ Trace: "trace",
48
+ Dataset: "dataset",
49
+ Verdict: "verdict",
50
+ Scorecard: "scorecard",
51
+ Findings: "findings",
52
+ Report: "report",
53
+ Source: "source",
54
+ Spec: "spec",
55
+ Config: "config",
56
+ } as const;
57
+ export type ArtifactKindValue = (typeof ArtifactKind)[keyof typeof ArtifactKind];
58
+
59
+ export const EscalationPolicy = {
60
+ Escalate: "escalate",
61
+ Abort: "abort",
62
+ Proceed: "proceed",
63
+ } as const;
64
+ export type EscalationPolicyValue =
65
+ (typeof EscalationPolicy)[keyof typeof EscalationPolicy];
66
+
67
+ // ── TypeBox schemas (closed objects — auditable boundary) ───────────────────
68
+ const SubjectSchema = Type.Object(
69
+ {
70
+ kind: Type.Union([Type.Literal(SubjectKind.Skill), Type.Literal(SubjectKind.Agent)]),
71
+ name: Type.String({ minLength: 1 }),
72
+ path: Type.String({ minLength: 1 }),
73
+ },
74
+ { additionalProperties: false },
75
+ );
76
+
77
+ const IntentSchema = Type.Object(
78
+ { command: Type.String({ minLength: 1 }), utterance: Type.Optional(Type.String()) },
79
+ { additionalProperties: false },
80
+ );
81
+
82
+ const ArtifactRefSchema = Type.Object(
83
+ {
84
+ id: Type.String({ minLength: 1 }),
85
+ kind: Type.Union([
86
+ Type.Literal(ArtifactKind.Trace),
87
+ Type.Literal(ArtifactKind.Dataset),
88
+ Type.Literal(ArtifactKind.Verdict),
89
+ Type.Literal(ArtifactKind.Scorecard),
90
+ Type.Literal(ArtifactKind.Findings),
91
+ Type.Literal(ArtifactKind.Report),
92
+ Type.Literal(ArtifactKind.Source),
93
+ Type.Literal(ArtifactKind.Spec),
94
+ Type.Literal(ArtifactKind.Config),
95
+ ]),
96
+ path: Type.Optional(Type.String({ minLength: 1 })),
97
+ uri: Type.Optional(Type.String({ minLength: 1 })),
98
+ sha: Type.Optional(Type.String({ minLength: 1 })),
99
+ bytes: Type.Optional(Type.Integer({ minimum: 0 })),
100
+ },
101
+ { additionalProperties: false },
102
+ );
103
+
104
+ const PartialLoadSchema = Type.Object(
105
+ { path: Type.String({ minLength: 1 }), reason: Type.String({ minLength: 1 }) },
106
+ { additionalProperties: false },
107
+ );
108
+
109
+ const ContextPackSchema = Type.Object(
110
+ {
111
+ rules: Type.Array(Type.String()),
112
+ memory: Type.Array(Type.String()),
113
+ partial_loads: Type.Array(PartialLoadSchema),
114
+ },
115
+ { additionalProperties: false },
116
+ );
117
+
118
+ const AcceptanceSchema = Type.Object(
119
+ { goal: Type.String({ minLength: 1 }), criteria: Type.Array(Type.String()) },
120
+ { additionalProperties: false },
121
+ );
122
+
123
+ const ProvenanceSchema = Type.Object(
124
+ { produced_by: Type.String({ minLength: 1 }), produced_at: Type.String({ minLength: 1 }) },
125
+ { additionalProperties: false },
126
+ );
127
+
128
+ const HandoverBundleSchema = Type.Object(
129
+ {
130
+ bundle_version: Type.Literal(HANDOVER_BUNDLE_VERSION),
131
+ adl_stage: Type.Union([
132
+ Type.Literal(AdlStage.Build),
133
+ Type.Literal(AdlStage.Evaluate),
134
+ Type.Literal(AdlStage.Diagnose),
135
+ Type.Literal(AdlStage.Improve),
136
+ Type.Literal(AdlStage.Audit),
137
+ ]),
138
+ subject: SubjectSchema,
139
+ intent: IntentSchema,
140
+ inputs: Type.Array(ArtifactRefSchema),
141
+ context_pack: ContextPackSchema,
142
+ acceptance: AcceptanceSchema,
143
+ provenance: ProvenanceSchema,
144
+ escalation_policy: Type.Union([
145
+ Type.Literal(EscalationPolicy.Escalate),
146
+ Type.Literal(EscalationPolicy.Abort),
147
+ Type.Literal(EscalationPolicy.Proceed),
148
+ ]),
149
+ },
150
+ { additionalProperties: false },
151
+ );
152
+ export type HandoverBundle = Static<typeof HandoverBundleSchema>;
153
+ export type ArtifactRef = Static<typeof ArtifactRefSchema>;
154
+
155
+ const BundleChecker = TypeCompiler.Compile(HandoverBundleSchema);
156
+
157
+ // ── Input ───────────────────────────────────────────────────────────────────
158
+
159
+ /**
160
+ * One routed failure. NOTE: there is deliberately NO `fix`/`remediation`/
161
+ * `patch` field — the evaluator is judge-only. The downstream diagnose stage
162
+ * decides remedies; the evaluator only states WHAT failed and WHY (critique).
163
+ */
164
+ export interface FailureRef {
165
+ criterionId: string;
166
+ severity: string;
167
+ flag: CriterionFlagValue;
168
+ traceId: string;
169
+ result: OutcomeVerdictValue;
170
+ critique: string;
171
+ /** GA — set iff `result === uncertain` (indeterminate); types the calibration route. */
172
+ blockedBy?: VerdictBlock;
173
+ }
174
+
175
+ /**
176
+ * GA — partition routed rows by verdict: a true FAIL routes to diagnostics
177
+ * (root-cause it), an INDETERMINATE (`uncertain`) routes to the CALIBRATION loop
178
+ * (re-ground / operator-ratify / re-scope by `blockedBy.kind`) — NEVER to
179
+ * diagnostics, because there is no confirmed defect to root-cause yet. PURE.
180
+ */
181
+ export interface RoutingPartition {
182
+ toDiagnostics: FailureRef[];
183
+ toCalibrate: FailureRef[];
184
+ }
185
+ export function partitionRouting(failures: FailureRef[]): RoutingPartition {
186
+ const toDiagnostics: FailureRef[] = [];
187
+ const toCalibrate: FailureRef[] = [];
188
+ for (const f of failures) {
189
+ if (f.result === OutcomeVerdict.Uncertain) toCalibrate.push(f);
190
+ else if (f.result === OutcomeVerdict.Fail) toDiagnostics.push(f);
191
+ // pass rows never appear in a failure list; ignore defensively.
192
+ }
193
+ return { toDiagnostics, toCalibrate };
194
+ }
195
+
196
+ /** GA — one calibration-loop item for an indeterminate verdict. The route is the
197
+ * `blockedBy.kind`: factual-intent→re-ground · normative→operator · scope→re-scope. */
198
+ export interface CalibrationItem {
199
+ criterionId: string;
200
+ severity: string;
201
+ traceId: string;
202
+ blockedBy: VerdictBlock;
203
+ route: "re-ground" | "operator" | "re-scope";
204
+ }
205
+ const KIND_ROUTE: Record<string, CalibrationItem["route"]> = {
206
+ "factual-intent": "re-ground",
207
+ normative: "operator",
208
+ scope: "re-scope",
209
+ };
210
+ export function calibrationItems(failures: FailureRef[]): CalibrationItem[] {
211
+ return partitionRouting(failures).toCalibrate.map((f) => {
212
+ const blockedBy: VerdictBlock = f.blockedBy ?? {
213
+ kind: "factual-intent",
214
+ text: f.critique,
215
+ };
216
+ return {
217
+ criterionId: f.criterionId,
218
+ severity: f.severity,
219
+ traceId: f.traceId,
220
+ blockedBy,
221
+ route: KIND_ROUTE[blockedBy.kind] ?? "re-ground",
222
+ };
223
+ });
224
+ }
225
+
226
+ export interface RouteFailuresInput {
227
+ subject: { kind: SubjectKindValue; name: string; path: string };
228
+ failures: FailureRef[];
229
+ artifacts: ArtifactRef[];
230
+ /** INJECTED provenance — never a self-read clock (determinism). */
231
+ producedBy: string;
232
+ producedAt: string;
233
+ escalationPolicy?: EscalationPolicyValue;
234
+ /** optional curated context that crossed the boundary. */
235
+ contextRules?: string[];
236
+ contextMemory?: string[];
237
+ partialLoads?: { path: string; reason: string }[];
238
+ }
239
+
240
+ /**
241
+ * Build the diagnose-stage HandoverBundle for the routed failures. Judge-only:
242
+ * no remedy is emitted — the failing criteria become the acceptance criteria
243
+ * the diagnose stage must root-cause. PURE + deterministic.
244
+ */
245
+ export function routeFailures(input: RouteFailuresInput): HandoverBundle {
246
+ // GA — only true FAILs are root-causable defects. Indeterminates route to the
247
+ // calibration loop (see `calibrationItems`), never into a diagnose handoff.
248
+ const criteria = partitionRouting(input.failures).toDiagnostics.map(
249
+ (f) =>
250
+ `${f.criterionId} [${f.severity}/${f.flag}] FAILED on trace ${f.traceId}: ${f.critique}`,
251
+ );
252
+ return {
253
+ bundle_version: HANDOVER_BUNDLE_VERSION,
254
+ adl_stage: AdlStage.Diagnose,
255
+ subject: { kind: input.subject.kind, name: input.subject.name, path: input.subject.path },
256
+ intent: { command: "*diagnose" },
257
+ inputs: input.artifacts,
258
+ context_pack: {
259
+ rules: input.contextRules ?? [],
260
+ memory: input.contextMemory ?? [],
261
+ partial_loads: input.partialLoads ?? [],
262
+ },
263
+ acceptance: {
264
+ goal:
265
+ "Root-cause the routed eval failures. JUDGE-ONLY handoff: the evaluator " +
266
+ "flags + routes, it does not repair — the diagnose stage owns the " +
267
+ "downstream repair.",
268
+ criteria,
269
+ },
270
+ provenance: { produced_by: input.producedBy, produced_at: input.producedAt },
271
+ escalation_policy: input.escalationPolicy ?? EscalationPolicy.Escalate,
272
+ };
273
+ }
274
+
275
+ /**
276
+ * MASK-ON-HANDOFF (carry). Serialize the handover bundle for EMIT through the
277
+ * byte-identity masker (`mask.ts`): `provenance.produced_at` (an injected
278
+ * timestamp) → `<TS>` and any absolute home path that slipped into an artifact
279
+ * locator → `<ABSPATH>`. RELATIVE artifact paths survive untouched (they are
280
+ * portable, not a leak).
281
+ *
282
+ * WHY: the evaluator's OWN diagnose handoff is itself a run-bundle the data-leak
283
+ * audit inspects. An un-masked `produced_at` + abs home path is exactly the
284
+ * operational leak the audit flags — so the evaluator dogfoods its own audit by
285
+ * masking on emit. It also preserves C-PIN: two runs (or two machines) that
286
+ * differ ONLY in timestamp / home-root serialize to a BYTE-IDENTICAL handoff.
287
+ *
288
+ * The in-memory `routeFailures` bundle keeps its real values (the structured
289
+ * object is unchanged); ONLY the emitted serialization is masked.
290
+ */
291
+ export function serializeHandoverBundle(bundle: HandoverBundle): string {
292
+ return maskedCanonicalJson(bundle);
293
+ }
294
+
295
+ export interface ValidationResult {
296
+ ok: boolean;
297
+ errors: string[];
298
+ }
299
+
300
+ /**
301
+ * Validate a candidate bundle against the closed contract + the "every
302
+ * artifact has at least one locator (path|uri)" rule TypeBox alone can't
303
+ * express. A smuggled undeclared field fails (additionalProperties:false).
304
+ */
305
+ export function validateHandoverBundle(candidate: unknown): ValidationResult {
306
+ const errors: string[] = [];
307
+ if (!BundleChecker.Check(candidate)) {
308
+ for (const e of BundleChecker.Errors(candidate)) {
309
+ errors.push(`${e.path}: ${e.message}`);
310
+ }
311
+ return { ok: false, errors };
312
+ }
313
+ const bundle = candidate as HandoverBundle;
314
+ bundle.inputs.forEach((a, i) => {
315
+ if (a.path === undefined && a.uri === undefined) {
316
+ errors.push(`inputs/${i}: artifact '${a.id}' has neither path nor uri`);
317
+ }
318
+ });
319
+ return { ok: errors.length === 0, errors };
320
+ }
@@ -0,0 +1,271 @@
1
+ /**
2
+ * scripts/run-deterministic.ts
3
+ * ---------------------------------------------------------------------------
4
+ * The deterministic executor — Tab-1 deterministic rows (decision #4). Walks the
5
+ * subject eval-matrix; for every criterion whose checkMethod is in the
6
+ * DETERMINISTIC track (deterministic-script | typebox-schema | gate) it runs a
7
+ * binary pass/fail check with NO model. Judge-track rows (trace-cross-ref |
8
+ * trajectory-diff) are emitted as `track:judge, result:skip` placeholders that
9
+ * the pinned-judge seam (run-judge.ts) fills in.
10
+ *
11
+ * The deterministic checks are REAL and grounded in the loaded run-bundle:
12
+ * - typebox-schema : the relevant produced artifact must be present + parse as
13
+ * a non-empty object/array (the schema-conformance proxy the
14
+ * bundle can answer offline).
15
+ * - gate : the gate's evidence must be present (e.g. an evidence/
16
+ * file set, a wave6 stamp) — absence => fail-loud.
17
+ * - deterministic-script : the artifact the script produces must be present and
18
+ * non-empty in the bundle.
19
+ *
20
+ * Where a criterion's deep semantic check genuinely needs to re-execute the
21
+ * subject's own script against live data (beyond presence/shape), that is an
22
+ * EXPLICIT, documented integration seam (see `evaluateDeterministic` -> the
23
+ * `needsLiveReexec` path) rather than a silent pass. Such rows return result
24
+ * `skip` with a detail explaining the seam, so they never FALSE-PASS.
25
+ *
26
+ * Pure + deterministic: components/criteria are processed in matrix order; no
27
+ * clock/random/network.
28
+ */
29
+ import {
30
+ type Component,
31
+ type Criterion,
32
+ type EvalMatrix,
33
+ type RowResultValue,
34
+ type ScorecardCriterion,
35
+ RowResult,
36
+ Track,
37
+ trackForCheckMethod,
38
+ } from "./contracts/types.ts";
39
+ import { type RunBundle } from "./contracts/types.ts";
40
+
41
+ export interface DeterministicRowResult {
42
+ componentId: string;
43
+ criterion: ScorecardCriterion;
44
+ }
45
+
46
+ /**
47
+ * Map a criterion to the artifact whose presence/shape proves it. Subject-
48
+ * agnostic heuristic: the bundle's well-known artifacts cover the produced
49
+ * outputs an audit can verify offline. A criterion that references no bundle
50
+ * artifact is a live-reexec seam.
51
+ */
52
+ function evidenceArtifactFor(
53
+ criterion: Criterion,
54
+ bundle: RunBundle,
55
+ ): string | null {
56
+ // Gate rows about evidence/aggregate require the evidence dir.
57
+ const s = criterion.statement.toLowerCase();
58
+ if (s.includes("evidence file") || s.includes("evidence/")) {
59
+ return bundle.data.evidence ? "evidence" : null;
60
+ }
61
+ if (s.includes("wave-6") || s.includes("wave6") || s.includes("stamp")) {
62
+ return bundle.data.wave6 ? "wave6" : null;
63
+ }
64
+ if (s.includes("runmeta") || s.includes("runmeta.")) {
65
+ return bundle.data.runMeta ? "runMeta" : null;
66
+ }
67
+ if (s.includes("entity") || s.includes("diagnosedentity")) {
68
+ return bundle.data.entityContext ? "entityContext" : null;
69
+ }
70
+ if (s.includes("renderinput") || s.includes("render input") || s.includes("heatmap")) {
71
+ return bundle.data.renderInput ? "renderInput" : null;
72
+ }
73
+ if (s.includes("traces-metadata") || s.includes("traces metadata")) {
74
+ return bundle.data.tracesMetadata ? "tracesMetadata" : null;
75
+ }
76
+ return null;
77
+ }
78
+
79
+ function isNonEmpty(value: unknown): boolean {
80
+ if (value == null) return false;
81
+ if (Array.isArray(value)) return value.length > 0;
82
+ if (typeof value === "object") return Object.keys(value).length > 0;
83
+ if (typeof value === "string") return value.length > 0;
84
+ return true;
85
+ }
86
+
87
+ export interface EvaluateOptions {
88
+ /**
89
+ * When false (default), criteria that need a LIVE re-exec of the subject's own
90
+ * script (beyond bundle presence/shape) return `skip` with a documented seam
91
+ * note instead of false-passing. A future integration may set this true and
92
+ * provide a re-exec callback.
93
+ */
94
+ liveReexec?: (criterion: Criterion) => RowResultValue | null;
95
+ }
96
+
97
+ /**
98
+ * Evaluate ONE deterministic criterion against the bundle. Never throws; returns
99
+ * a binary pass/fail, or `skip` with a documented reason for a live-reexec seam.
100
+ */
101
+ export function evaluateDeterministic(
102
+ criterion: Criterion,
103
+ bundle: RunBundle,
104
+ opts: EvaluateOptions = {},
105
+ ): ScorecardCriterion {
106
+ const track = Track.Deterministic;
107
+ const artifactKey = evidenceArtifactFor(criterion, bundle);
108
+
109
+ if (artifactKey == null) {
110
+ // No bundle artifact answers this row offline -> live-reexec seam.
111
+ const live = opts.liveReexec?.(criterion) ?? null;
112
+ if (live) {
113
+ return {
114
+ dimension: criterion.dimension,
115
+ severity: criterion.severity,
116
+ checkMethod: criterion.checkMethod,
117
+ track,
118
+ result: live,
119
+ detail: "evaluated via live-reexec callback",
120
+ };
121
+ }
122
+ return {
123
+ dimension: criterion.dimension,
124
+ severity: criterion.severity,
125
+ checkMethod: criterion.checkMethod,
126
+ track,
127
+ result: RowResult.Skip,
128
+ detail:
129
+ "INTEGRATION SEAM: requires live re-exec of the subject's own script (no bundle artifact answers this row offline); skipped rather than false-passed",
130
+ };
131
+ }
132
+
133
+ const present = isNonEmpty(bundle.data[artifactKey]);
134
+ return {
135
+ dimension: criterion.dimension,
136
+ severity: criterion.severity,
137
+ checkMethod: criterion.checkMethod,
138
+ track,
139
+ result: present ? RowResult.Pass : RowResult.Fail,
140
+ detail: present
141
+ ? `evidence artifact '${artifactKey}' present + non-empty`
142
+ : `evidence artifact '${artifactKey}' MISSING/empty in bundle (fail-loud)`,
143
+ };
144
+ }
145
+
146
+ /** Judge-track placeholder — filled in by the pinned-judge seam. */
147
+ export function judgePlaceholder(criterion: Criterion): ScorecardCriterion {
148
+ return {
149
+ dimension: criterion.dimension,
150
+ severity: criterion.severity,
151
+ checkMethod: criterion.checkMethod,
152
+ track: Track.Judge,
153
+ result: RowResult.Skip,
154
+ detail:
155
+ "PINNED-JUDGE SEAM: requires pinned model (id + temp=0) reading transcript vs behavior-tree; deferred to run-judge.ts",
156
+ };
157
+ }
158
+
159
+ export interface RunDeterministicResult {
160
+ rows: DeterministicRowResult[];
161
+ deterministicCount: number;
162
+ judgeCount: number;
163
+ }
164
+
165
+ // ── Coverage honesty (EV-OUT-002) ──────────────────────────────────────────
166
+ /**
167
+ * Default skip-rate at/above which a coverage WARNING is raised. Conservative
168
+ * (0.5) so a run where more rows were skipped than graded can no longer claim a
169
+ * silent PASS. This is a WARNING threshold ONLY — it never flips gate pass/fail.
170
+ */
171
+ export const DEFAULT_SKIP_RATE_WARN_THRESHOLD = 0.5;
172
+
173
+ /** Fixed precision for skipRate so two audits serialize byte-identically. */
174
+ const SKIP_RATE_PRECISION = 4;
175
+
176
+ /** The pass/fail/skip tally `assembleScorecard` derives from the graded rows. */
177
+ export interface CoverageTotals {
178
+ pass: number;
179
+ fail: number;
180
+ skip: number;
181
+ }
182
+
183
+ export interface CoverageOptions {
184
+ /**
185
+ * Skip-rate STRICTLY ABOVE which `coverageWarning` is set. Default
186
+ * {@link DEFAULT_SKIP_RATE_WARN_THRESHOLD}. Warning-only — this never alters
187
+ * the GATE's pass/fail decision; the honesty mechanism is the surfaced
188
+ * warning, not a gate flip.
189
+ */
190
+ skipRateWarnThreshold?: number;
191
+ }
192
+
193
+ export interface Coverage {
194
+ /** rows actually graded (pass + fail) — the non-vacuous denominator. */
195
+ graded: number;
196
+ /** every criterion (pass + fail + skip). */
197
+ total: number;
198
+ /** rows skipped (seam / no-bundle-artifact / judge placeholder). */
199
+ skipped: number;
200
+ /** skipped / total, rounded to fixed precision (0 when total is 0). */
201
+ skipRate: number;
202
+ /** the threshold that was applied. */
203
+ skipRateWarnThreshold: number;
204
+ /** true iff skipRate exceeds the threshold — surfaces a near-vacuous PASS. */
205
+ coverageWarning: boolean;
206
+ }
207
+
208
+ /**
209
+ * Derive the coverage-honesty stat from a pass/fail/skip tally. Pure +
210
+ * deterministic: integer counts in, fixed-precision ratio out, no clock/random.
211
+ *
212
+ * A high skip-rate means most criteria were never graded — so a `gateRunPass`
213
+ * of true is near-vacuous. `coverageWarning` makes that LOUD without changing
214
+ * pass/fail semantics (the gate is decided entirely upstream by fail counts).
215
+ */
216
+ export function computeCoverage(
217
+ totals: CoverageTotals,
218
+ opts: CoverageOptions = {},
219
+ ): Coverage {
220
+ const skipRateWarnThreshold =
221
+ opts.skipRateWarnThreshold ?? DEFAULT_SKIP_RATE_WARN_THRESHOLD;
222
+ const graded = totals.pass + totals.fail;
223
+ const skipped = totals.skip;
224
+ const total = graded + skipped;
225
+ const rawRate = total === 0 ? 0 : skipped / total;
226
+ const factor = 10 ** SKIP_RATE_PRECISION;
227
+ const skipRate = Math.round(rawRate * factor) / factor;
228
+ return {
229
+ graded,
230
+ total,
231
+ skipped,
232
+ skipRate,
233
+ skipRateWarnThreshold,
234
+ coverageWarning: skipRate > skipRateWarnThreshold,
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Run the full deterministic pass over a matrix + bundle. Emits one row per
240
+ * criterion: deterministic rows are graded now; judge rows are placeholders.
241
+ */
242
+ export function runDeterministic(
243
+ matrix: EvalMatrix,
244
+ bundle: RunBundle,
245
+ opts: EvaluateOptions = {},
246
+ ): RunDeterministicResult {
247
+ const rows: DeterministicRowResult[] = [];
248
+ let deterministicCount = 0;
249
+ let judgeCount = 0;
250
+
251
+ for (const component of matrix.components as Component[]) {
252
+ for (const criterion of component.criteria) {
253
+ const track = trackForCheckMethod(criterion.checkMethod);
254
+ if (track === Track.Deterministic) {
255
+ deterministicCount += 1;
256
+ rows.push({
257
+ componentId: component.componentId,
258
+ criterion: evaluateDeterministic(criterion, bundle, opts),
259
+ });
260
+ } else {
261
+ judgeCount += 1;
262
+ rows.push({
263
+ componentId: component.componentId,
264
+ criterion: judgePlaceholder(criterion),
265
+ });
266
+ }
267
+ }
268
+ }
269
+
270
+ return { rows, deterministicCount, judgeCount };
271
+ }