@mutagent/evaluator 0.1.0-alpha.6 → 0.2.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/skills/mutagent-evaluator/SKILL.md +23 -25
- package/.claude/skills/mutagent-evaluator/assets/agents/dataset-builder.md +6 -5
- package/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +77 -14
- package/.claude/skills/mutagent-evaluator/assets/templates/discover-report.template.html +397 -0
- package/.claude/skills/mutagent-evaluator/assets/templates/eval-report.template.html +594 -0
- package/.claude/skills/mutagent-evaluator/assets/templates/review-report.template.html +560 -0
- package/.claude/skills/mutagent-evaluator/golden/judge-trajectory.prose.md +69 -0
- package/.claude/skills/mutagent-evaluator/references/data-registry.md +43 -0
- package/.claude/skills/mutagent-evaluator/references/eval-layers.md +52 -0
- package/.claude/skills/mutagent-evaluator/references/eval-stage.md +1 -1
- package/.claude/skills/mutagent-evaluator/scripts/aggregate-discover.ts +254 -9
- package/.claude/skills/mutagent-evaluator/scripts/audience.ts +84 -0
- package/.claude/skills/mutagent-evaluator/scripts/code-eval-library.ts +201 -0
- package/.claude/skills/mutagent-evaluator/scripts/contracts/agentspec-evals.ts +101 -39
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +287 -3
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-types.ts +30 -4
- package/.claude/skills/mutagent-evaluator/scripts/materialize-dataset.ts +130 -68
- package/.claude/skills/mutagent-evaluator/scripts/matrix-judge.ts +219 -1
- package/.claude/skills/mutagent-evaluator/scripts/merge-criteria.ts +849 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-discover-report-v3.ts +1155 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-eval-report-v3.ts +610 -0
- package/.claude/skills/mutagent-evaluator/scripts/render-eval-report.ts +2 -1
- package/.claude/skills/mutagent-evaluator/scripts/render-review-report-v3.ts +691 -0
- package/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +10 -7
- package/.claude/skills/mutagent-evaluator/scripts/run-evaluate.ts +46 -1
- package/.claude/skills/mutagent-evaluator/scripts/run-review.ts +266 -0
- package/.claude/skills/mutagent-evaluator/scripts/verify-render.ts +728 -0
- package/package.json +2 -2
|
@@ -91,6 +91,15 @@ export const MatrixCriterionSchema = Type.Object(
|
|
|
91
91
|
]),
|
|
92
92
|
/** the 3-dimension MECE coverage tag (optional context for the judge). */
|
|
93
93
|
dimension: Type.Optional(Type.String()),
|
|
94
|
+
/**
|
|
95
|
+
* WHICH evidence layer this criterion binds to (the v3 layered walk's own
|
|
96
|
+
* taxonomy): a judge-walk layer id (`"L0".."L4"`), a discover-proposed
|
|
97
|
+
* MetricLevel (`"output" | "context" | "cross-stage"`), or the criteria
|
|
98
|
+
* super-layer (`"criteria"`) for a cross-cutting check. ADDITIVE/OPTIONAL —
|
|
99
|
+
* absent ⇒ renderers default to `"criteria"` (byte-stable pre-v3 rows).
|
|
100
|
+
* Tolerant free string for forward-compat; consumers map to LAYER_NAMES.
|
|
101
|
+
*/
|
|
102
|
+
layer: Type.Optional(Type.String()),
|
|
94
103
|
/** what the judge should read for this row (write-judge-prompt "choose what to pass"). */
|
|
95
104
|
judgeInputs: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
96
105
|
// ── T1 (B-U3) tier-0 router (ADDITIVE, OPTIONAL — grandfather) ──────────────
|
|
@@ -138,6 +147,7 @@ export function minedToMatrixCriterion(c: MinedCriterion): MatrixCriterion {
|
|
|
138
147
|
passCondition: c.statement,
|
|
139
148
|
severity: c.metadata.severity,
|
|
140
149
|
dimension: c.metadata.dimension,
|
|
150
|
+
layer: c.metadata.level,
|
|
141
151
|
checkMethod: c.metadata.check_method,
|
|
142
152
|
...(judgeInputs.length > 0 ? { judgeInputs } : {}),
|
|
143
153
|
...(c.codeEval !== undefined ? { codeEval: c.codeEval } : {}),
|
|
@@ -281,6 +291,14 @@ export const MatrixPacketSchema = Type.Object(
|
|
|
281
291
|
{ model: Type.String({ minLength: 1 }), temperature: Type.Literal(0) },
|
|
282
292
|
{ additionalProperties: false },
|
|
283
293
|
),
|
|
294
|
+
/**
|
|
295
|
+
* v3 LAYERED WALK (D1=B+) — layer-scope filter for the pipeable
|
|
296
|
+
* `*evaluate-<layer>` commands: absent/empty = the FULL walk (L0→L4 +
|
|
297
|
+
* criteria); a subset = the judge runs ONLY those layer phases (criteria
|
|
298
|
+
* still verdicted over whatever those phases gather — never left
|
|
299
|
+
* unverdicted). ADDITIVE/OPTIONAL: legacy packets still validate.
|
|
300
|
+
*/
|
|
301
|
+
layerScope: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
284
302
|
},
|
|
285
303
|
{ additionalProperties: false },
|
|
286
304
|
);
|
|
@@ -392,7 +410,9 @@ export type ExpectedStep = Static<typeof ExpectedStepSchema>;
|
|
|
392
410
|
export const AgentStepSchema = Type.Object(
|
|
393
411
|
{
|
|
394
412
|
n: Type.Number(),
|
|
395
|
-
|
|
413
|
+
/** TOLERANT: explicit `null` = an honest "no tool call this step" (live-judge shape
|
|
414
|
+
* on a zero-tool trajectory) — distinct from an omitted field. */
|
|
415
|
+
tool: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
396
416
|
/** ok | error | warn | false-success */
|
|
397
417
|
status: Type.Optional(Type.String()),
|
|
398
418
|
detail: Type.Optional(Type.String()),
|
|
@@ -448,6 +468,139 @@ export const CandidateItemSchema = Type.Object(
|
|
|
448
468
|
);
|
|
449
469
|
export type CandidateItem = Static<typeof CandidateItemSchema>;
|
|
450
470
|
|
|
471
|
+
// ── v3 LAYERED WALK (E-NEW-1..6,9 — ADDITIVE, OPTIONAL) ──────────────────────
|
|
472
|
+
// Evaluator-v3 contract (operator-frozen 2026-07-24): ONE judge per trajectory
|
|
473
|
+
// performs the whole evidence-layer walk internally — L0 code checks → L1
|
|
474
|
+
// outcome → (descend per stopping rules) L2 trajectory → L3 tool outputs → L4
|
|
475
|
+
// context — and EMITS one LayerVerdict per engaged layer alongside the
|
|
476
|
+
// (unchanged) per-criterion verdicts. Cross-layer disagreement is SIGNAL:
|
|
477
|
+
// aggregate DETECTS conflicts, never resolves them (D2-A / OT-1). All fields
|
|
478
|
+
// OPTIONAL (grandfather): pre-v3 verdict files still validate; renderers show
|
|
479
|
+
// NAMED degrade badges, never silent blanks.
|
|
480
|
+
|
|
481
|
+
/** The five evidence layers of the walk. */
|
|
482
|
+
export const LayerId = {
|
|
483
|
+
Code: "L0",
|
|
484
|
+
Outcome: "L1",
|
|
485
|
+
Trajectory: "L2",
|
|
486
|
+
ToolOutputs: "L3",
|
|
487
|
+
Context: "L4",
|
|
488
|
+
} as const;
|
|
489
|
+
export type LayerIdValue = (typeof LayerId)[keyof typeof LayerId];
|
|
490
|
+
export const LAYER_IDS: readonly string[] = Object.values(LayerId);
|
|
491
|
+
|
|
492
|
+
/** Human names — the vocabulary reports print beside the L-codes. */
|
|
493
|
+
export const LAYER_NAMES: Record<string, string> = {
|
|
494
|
+
L0: "code checks",
|
|
495
|
+
L1: "outcome",
|
|
496
|
+
L2: "trajectory",
|
|
497
|
+
L3: "tool outputs",
|
|
498
|
+
L4: "context",
|
|
499
|
+
};
|
|
500
|
+
|
|
501
|
+
/** One per-layer verdict from the walk (E-NEW-1). `skipped` marks layers the
|
|
502
|
+
* stopping rules never engaged (early-exit) — skipped ≠ undecidable. */
|
|
503
|
+
export const LayerVerdictSchema = Type.Object(
|
|
504
|
+
{
|
|
505
|
+
/** L0 | L1 | L2 | L3 | L4 (tolerant free string for forward-compat). */
|
|
506
|
+
layer: Type.String({ minLength: 1 }),
|
|
507
|
+
verdict: Type.Union([
|
|
508
|
+
Type.Literal("pass"),
|
|
509
|
+
Type.Literal("fail"),
|
|
510
|
+
Type.Literal("undecidable"),
|
|
511
|
+
Type.Literal("skipped"),
|
|
512
|
+
/** L0 EVIDENCE-STATE (live-judge shape): patterns fired, evidence recorded —
|
|
513
|
+
* honoring the meta-judge fence a pass/fail on L0 would violate (a fired
|
|
514
|
+
* pattern is a candidate, not a verdict). L0-specific. */
|
|
515
|
+
Type.Literal("fired"),
|
|
516
|
+
/** L0 CLEAN-STATE (live-judge shape): the library RAN and NO pattern fired.
|
|
517
|
+
* Distinct from `pass` (a pass would assert L0 proved the layer correct) and
|
|
518
|
+
* from `skipped` (the library did run). "no code-detectable candidate" — an
|
|
519
|
+
* honest clean, never a verdict on the deeper layers. */
|
|
520
|
+
Type.Literal("no-fires"),
|
|
521
|
+
]),
|
|
522
|
+
/** annotation-only — may NEVER flip the verdict. */
|
|
523
|
+
confidence: Type.Optional(Type.Number()),
|
|
524
|
+
/** grounding citations (same tolerant shape as judge-step refs). */
|
|
525
|
+
refs: Type.Optional(Type.Array(Type.Union([Type.String(), GaRefSchema]))),
|
|
526
|
+
/** one-breath reasoning for THIS layer's verdict. */
|
|
527
|
+
note: Type.Optional(Type.String()),
|
|
528
|
+
/** L1: the classified scenario (STRUCTURED, not prose — E-NEW-2). */
|
|
529
|
+
scenario: Type.Optional(Type.String()),
|
|
530
|
+
/** L1: the expected terminal actions for that scenario (E-NEW-2). */
|
|
531
|
+
expectedExit: Type.Optional(Type.Array(Type.String())),
|
|
532
|
+
/** L3: the first problematic observation — everything downstream suspect. */
|
|
533
|
+
/** TOLERANT: explicit `null` = an honest "no problem origin exists" (live-judge
|
|
534
|
+
* shape — naming one would invent it), distinct from an omitted field. */
|
|
535
|
+
firstProblemOrigin: Type.Optional(Type.Union([Type.String(), Type.Null()])),
|
|
536
|
+
/**
|
|
537
|
+
* L4: retrieval (never fetched) vs attention (present but ignored).
|
|
538
|
+
*
|
|
539
|
+
* TOLERANT-BUT-STILL-CLASSIFIED. A live judge emitted the required distinction
|
|
540
|
+
* and then QUALIFIED it — "attention (transient, non-terminal — self-corrected
|
|
541
|
+
* before send); no retrieval gap" — which the bare 2-literal union rejected.
|
|
542
|
+
* Both forms are now accepted, but deliberately NOT by loosening to a free
|
|
543
|
+
* string: the qualified form must still BEGIN with one of the two kinds, so
|
|
544
|
+
* the classification stays machine-extractable (read the leading token) and a
|
|
545
|
+
* value that classifies nothing (`"unclear"`) is still refused. Loosening to
|
|
546
|
+
* `Type.String()` would have kept this file parsing while silently forfeiting
|
|
547
|
+
* the fail-loud property the field exists for.
|
|
548
|
+
*/
|
|
549
|
+
gapKind: Type.Optional(
|
|
550
|
+
Type.Union([
|
|
551
|
+
Type.Literal("retrieval"),
|
|
552
|
+
Type.Literal("attention"),
|
|
553
|
+
Type.String({ pattern: "^(retrieval|attention)\\b" }),
|
|
554
|
+
]),
|
|
555
|
+
),
|
|
556
|
+
/**
|
|
557
|
+
* L2: the ONE-LINE statement of where the actual path left the expected path
|
|
558
|
+
* — the headline of a trajectory-layer failure, distilled out of the much
|
|
559
|
+
* longer `note`. A live judge emitted it alongside a `note` that walks the
|
|
560
|
+
* full chronological path, i.e. it is the summary a reader needs, not a
|
|
561
|
+
* duplicate: "mandatory reviewWithManager step omitted between draft
|
|
562
|
+
* generation and the terminal sendMessage".
|
|
563
|
+
*/
|
|
564
|
+
divergence: Type.Optional(Type.String()),
|
|
565
|
+
},
|
|
566
|
+
{ additionalProperties: false },
|
|
567
|
+
);
|
|
568
|
+
export type LayerVerdict = Static<typeof LayerVerdictSchema>;
|
|
569
|
+
|
|
570
|
+
/** E-NEW-6 — one L0 code-eval library hit (the judge runs the library itself). */
|
|
571
|
+
export const CodeEvalHitSchema = Type.Object(
|
|
572
|
+
{
|
|
573
|
+
/** the library pattern id (e.g. CE-P1 fault-passthrough). */
|
|
574
|
+
pattern: Type.String({ minLength: 1 }),
|
|
575
|
+
/** where it fired (obs id / step anchor). */
|
|
576
|
+
anchor: Type.Optional(Type.Union([Type.Number(), Type.String()])),
|
|
577
|
+
detail: Type.Optional(Type.String()),
|
|
578
|
+
},
|
|
579
|
+
{ additionalProperties: false },
|
|
580
|
+
);
|
|
581
|
+
export type CodeEvalHit = Static<typeof CodeEvalHitSchema>;
|
|
582
|
+
|
|
583
|
+
/** E-NEW-9b — the walk's SELF-MANIFEST of emitted vs missing emissions; AGGREGATE
|
|
584
|
+
* folds these into the MR-5 data-completeness scorecard so a skipped expected
|
|
585
|
+
* emission is caught the same run, not at render time. */
|
|
586
|
+
export const WalkEmissionsSchema = Type.Object(
|
|
587
|
+
{
|
|
588
|
+
/** emission keys the judge DID produce. */
|
|
589
|
+
emitted: Type.Array(Type.String()),
|
|
590
|
+
/** expected-but-missing keys, each with the judge's own reason. */
|
|
591
|
+
missing: Type.Optional(
|
|
592
|
+
Type.Array(
|
|
593
|
+
Type.Object(
|
|
594
|
+
{ key: Type.String({ minLength: 1 }), reason: Type.String({ minLength: 1 }) },
|
|
595
|
+
{ additionalProperties: false },
|
|
596
|
+
),
|
|
597
|
+
),
|
|
598
|
+
),
|
|
599
|
+
},
|
|
600
|
+
{ additionalProperties: false },
|
|
601
|
+
);
|
|
602
|
+
export type WalkEmissions = Static<typeof WalkEmissionsSchema>;
|
|
603
|
+
|
|
451
604
|
// ── MatrixVerdictFile — the Judge Agent OUTPUT (one per trajectory) ──────────
|
|
452
605
|
export const MatrixVerdictFileSchema = Type.Object(
|
|
453
606
|
{
|
|
@@ -508,10 +661,91 @@ export const MatrixVerdictFileSchema = Type.Object(
|
|
|
508
661
|
Type.String(),
|
|
509
662
|
Type.Object(
|
|
510
663
|
{
|
|
511
|
-
root
|
|
664
|
+
/** The primary root (root-not-symptom). REQUIRED on a structured localize —
|
|
665
|
+
* a rootless walk emits `null` (an honest "no failure to localize"), and a
|
|
666
|
+
* judge that has only a `summary` has its root FILLED from it at parse time
|
|
667
|
+
* (see the coercion below) so the invariant holds without rejecting honest
|
|
668
|
+
* output. */
|
|
669
|
+
root: Type.Union([Type.String({ minLength: 1 }), Type.Null()]),
|
|
670
|
+
/**
|
|
671
|
+
* A short SUMMARY a judge emits when there is NO failure to localize to a
|
|
672
|
+
* single root (a clean/hold trajectory with independent non-criterion
|
|
673
|
+
* deviations). Live-judge shape: instead of `root`, it summarises the whole
|
|
674
|
+
* trajectory and routes the deviations to `candidates`. Consumers coerce
|
|
675
|
+
* either to a readable band (`root` preferred, `summary` fallback).
|
|
676
|
+
*/
|
|
677
|
+
summary: Type.Optional(Type.String()),
|
|
512
678
|
symptom: Type.Optional(Type.String()),
|
|
679
|
+
/** grounding prose for the root claim (first emitted by a live D1 judge). */
|
|
680
|
+
evidence: Type.Optional(Type.String()),
|
|
681
|
+
/** the criterionIds this root OWNS (judge-emitted consolidate-by-locus hint). */
|
|
682
|
+
criteria: Type.Optional(Type.Array(Type.String())),
|
|
683
|
+
note: Type.Optional(Type.String()),
|
|
513
684
|
ref: Type.Optional(Type.Union([Type.String(), GaRefSchema])),
|
|
514
685
|
routeTo: Type.Optional(Type.String()),
|
|
686
|
+
/**
|
|
687
|
+
* FURTHER roots that are INDEPENDENT of `root` — not steps in one causal
|
|
688
|
+
* chain, but separate failures that happen to appear in the same
|
|
689
|
+
* trajectory. Emitted by a live judge following `root_not_symptom`'s
|
|
690
|
+
* "multiple INDEPENDENT roots ⇒ multiple criteria": it kept the primary
|
|
691
|
+
* root in `root` and listed the rest here rather than silently collapsing
|
|
692
|
+
* them into one locus. Consolidate-by-locus clusters on `root`; these are
|
|
693
|
+
* additional loci, so a consumer that reads only `root` UNDER-REPORTS.
|
|
694
|
+
*/
|
|
695
|
+
independentRoots: Type.Optional(Type.Array(Type.String())),
|
|
696
|
+
/**
|
|
697
|
+
* The L2 actual-vs-expected divergence. Accepted HERE as well as on the
|
|
698
|
+
* layer verdict because the OPERATOR-FROZEN judge prose explicitly says
|
|
699
|
+
* "(L2 trajectory) … emit layerVerdicts L2 (+ divergence in localize)" —
|
|
700
|
+
* so a judge obeying the contract literally puts it in this object, and
|
|
701
|
+
* the closed schema rejected exactly that. (The live D1 judge happened to
|
|
702
|
+
* put it on `layerVerdicts[L2]` instead, which is why this never fired.)
|
|
703
|
+
* Same class as the `rootCause`/`firstThingWrong` drift: contract prose
|
|
704
|
+
* naming a field the schema refuses. The prose is C-PIN golden, so the
|
|
705
|
+
* SCHEMA accepts both placements rather than churning a frozen prompt.
|
|
706
|
+
*/
|
|
707
|
+
divergence: Type.Optional(Type.String()),
|
|
708
|
+
/**
|
|
709
|
+
* An OT-1 conflict the judge surfaced UNRECONCILED (e.g. the outcome layer
|
|
710
|
+
* passes while the trajectory layer fails). Doctrine is to expose the
|
|
711
|
+
* disagreement for a human rather than have the judge resolve it, so this
|
|
712
|
+
* is prose stating both sides and explicitly declining to reconcile —
|
|
713
|
+
* frequently the single most decision-relevant field on the file.
|
|
714
|
+
*/
|
|
715
|
+
/**
|
|
716
|
+
* An OT-1 conflict the judge surfaced UNRECONCILED (e.g. the outcome layer
|
|
717
|
+
* passes while the trajectory layer fails). Doctrine is to expose the
|
|
718
|
+
* disagreement for a human rather than have the judge resolve it. TOLERANT:
|
|
719
|
+
* free prose, OR a structured {sideA, sideB, note?} pair (a live judge
|
|
720
|
+
* emitted the structured form; both carry the same meaning — two sides,
|
|
721
|
+
* explicitly not reconciled). Frequently the single most decision-relevant
|
|
722
|
+
* field on the file.
|
|
723
|
+
*/
|
|
724
|
+
conflict: Type.Optional(
|
|
725
|
+
Type.Union([
|
|
726
|
+
Type.String(),
|
|
727
|
+
Type.Object(
|
|
728
|
+
{
|
|
729
|
+
sideA: Type.String(),
|
|
730
|
+
sideB: Type.String(),
|
|
731
|
+
note: Type.Optional(Type.String()),
|
|
732
|
+
},
|
|
733
|
+
{ additionalProperties: false },
|
|
734
|
+
),
|
|
735
|
+
/** `null` = an honest "no conflict — the layers agree" (live-judge shape). */
|
|
736
|
+
Type.Null(),
|
|
737
|
+
]),
|
|
738
|
+
),
|
|
739
|
+
/**
|
|
740
|
+
* EV-051 routing prose. NOTE: this is the SAME CONCEPT as `routeTo` above,
|
|
741
|
+
* emitted under a second name by a real judge. Both are accepted because
|
|
742
|
+
* rejecting the file over a synonym is the worse failure — but do NOT add
|
|
743
|
+
* a third spelling: a closed schema carrying synonyms is how the
|
|
744
|
+
* `rootCause`/`firstThingWrong` drift started. `routeTo` currently has ZERO
|
|
745
|
+
* consumers, so consolidating onto ONE of these is a free cleanup whenever
|
|
746
|
+
* someone touches the routing path.
|
|
747
|
+
*/
|
|
748
|
+
routing: Type.Optional(Type.String()),
|
|
515
749
|
},
|
|
516
750
|
{ additionalProperties: false },
|
|
517
751
|
),
|
|
@@ -534,6 +768,13 @@ export const MatrixVerdictFileSchema = Type.Object(
|
|
|
534
768
|
]),
|
|
535
769
|
),
|
|
536
770
|
),
|
|
771
|
+
/**
|
|
772
|
+
* E-NEW additive (first emitted by a live D1 judge): per-criterion REASON a
|
|
773
|
+
* denseMap cell is `na` (`cid → why the scoping predicate is positively
|
|
774
|
+
* falsified`). `na` is the third accounting path of the completeness law —
|
|
775
|
+
* this carries its rationale so the report can display WHY, not just that.
|
|
776
|
+
*/
|
|
777
|
+
naRationale: Type.Optional(Type.Record(Type.String(), Type.String())),
|
|
537
778
|
/**
|
|
538
779
|
* §9.4.2 node 1 — the EXAMINE fidelity gate. `complete:false` means the trace
|
|
539
780
|
* was truncated / unreadable → the trajectory EXITS early as INCOMPLETE (no
|
|
@@ -547,6 +788,19 @@ export const MatrixVerdictFileSchema = Type.Object(
|
|
|
547
788
|
),
|
|
548
789
|
/** §9.4.2 node 2.5 — candidate eval/dataset items for UNMATCHED detections. */
|
|
549
790
|
candidates: Type.Optional(Type.Array(CandidateItemSchema)),
|
|
791
|
+
// ── v3 LAYERED WALK emissions (E-NEW — OPTIONAL/additive, pre-v3 files validate) ──
|
|
792
|
+
/** E-NEW-1 — one verdict per engaged evidence layer (skipped ≠ undecidable). */
|
|
793
|
+
layerVerdicts: Type.Optional(Type.Array(LayerVerdictSchema)),
|
|
794
|
+
/** E-NEW-1 — which layer (if any) the stopping rules exited at (e.g. "L1"). */
|
|
795
|
+
earlyExit: Type.Optional(Type.String()),
|
|
796
|
+
/** E-NEW-1/9 — how many layers the walk actually engaged (economy stat).
|
|
797
|
+
* TOLERANT: a count OR the engaged layer-id list (live-judge shape,
|
|
798
|
+
* e.g. ["L0","L1","L2"] — richer; consumers coerce via length). */
|
|
799
|
+
layersEngaged: Type.Optional(Type.Union([Type.Number(), Type.Array(Type.String())])),
|
|
800
|
+
/** E-NEW-6 — L0 code-eval library hits the judge recorded (it runs the library itself). */
|
|
801
|
+
codeEvalHits: Type.Optional(Type.Array(CodeEvalHitSchema)),
|
|
802
|
+
/** E-NEW-9b — the walk's self-manifest of emitted vs missing emissions (→ MR-5). */
|
|
803
|
+
emissions: Type.Optional(WalkEmissionsSchema),
|
|
550
804
|
/**
|
|
551
805
|
* the per-trajectory judge SELF-REPORTED health micro-summary. NOTE: this is
|
|
552
806
|
* IGNORED for the report's health roll-up — T4 DERIVES health from the walk
|
|
@@ -578,7 +832,9 @@ export type MatrixVerdictFile = Static<typeof MatrixVerdictFileSchema>;
|
|
|
578
832
|
export function localizeText(localize: MatrixVerdictFile["localize"]): string {
|
|
579
833
|
if (localize === undefined) return "";
|
|
580
834
|
if (typeof localize === "string") return localize;
|
|
581
|
-
|
|
835
|
+
// `null` root = an honest "no failure to localize" — the summary carries the band.
|
|
836
|
+
const root = localize.root ?? localize.summary ?? "";
|
|
837
|
+
return localize.symptom !== undefined ? `${root} (symptom: ${localize.symptom})` : root;
|
|
582
838
|
}
|
|
583
839
|
|
|
584
840
|
/**
|
|
@@ -594,6 +850,34 @@ export function parseMatrixVerdictFile(raw: string): MatrixVerdictFile {
|
|
|
594
850
|
} catch {
|
|
595
851
|
throw new Error(`parseMatrixVerdictFile: not valid JSON: ${raw.slice(0, 120)}`);
|
|
596
852
|
}
|
|
853
|
+
// COERCE the live-judge rootless-localize: a judge with NO single root to localize
|
|
854
|
+
// emits a structured localize WITHOUT `root` — carrying a `summary` (clean/hold
|
|
855
|
+
// trajectory) or a `conflict` (a real disagreement whose disagreement IS the thing to
|
|
856
|
+
// localize). Rather than weaken the contract's required-root invariant (a guard test
|
|
857
|
+
// asserts a structured localize with no root is refused), FILL the root from the
|
|
858
|
+
// summary, or from the conflict's own text, so honest rootless output is accepted AND
|
|
859
|
+
// the invariant holds. NOTHING is discarded (root takes precedence when present). A
|
|
860
|
+
// structured localize with NONE of root/summary/conflict still fails — the guard shape.
|
|
861
|
+
if (
|
|
862
|
+
parsed !== null &&
|
|
863
|
+
typeof parsed === "object" &&
|
|
864
|
+
!Array.isArray(parsed) &&
|
|
865
|
+
(parsed as Record<string, unknown>)["localize"] !== null &&
|
|
866
|
+
typeof (parsed as Record<string, unknown>)["localize"] === "object"
|
|
867
|
+
) {
|
|
868
|
+
const loc = (parsed as Record<string, unknown>)["localize"] as Record<string, unknown>;
|
|
869
|
+
if (loc["root"] === undefined) {
|
|
870
|
+
if (typeof loc["summary"] === "string") {
|
|
871
|
+
loc["root"] = loc["summary"];
|
|
872
|
+
} else if (typeof loc["conflict"] === "string") {
|
|
873
|
+
loc["root"] = loc["conflict"];
|
|
874
|
+
} else if (loc["conflict"] !== null && typeof loc["conflict"] === "object") {
|
|
875
|
+
const c = loc["conflict"] as Record<string, unknown>;
|
|
876
|
+
const parts = [c["sideA"], c["sideB"], c["note"]].filter((x): x is string => typeof x === "string");
|
|
877
|
+
if (parts.length > 0) loc["root"] = parts.join(" · ");
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
597
881
|
if (!Value.Check(MatrixVerdictFileSchema, parsed)) {
|
|
598
882
|
const first = [...Value.Errors(MatrixVerdictFileSchema, parsed)][0];
|
|
599
883
|
throw new Error(
|
|
@@ -642,6 +642,22 @@ export interface DiscoveryRationale {
|
|
|
642
642
|
export interface MinedCriterion extends DiscoveredCriterion {
|
|
643
643
|
metadata: MetricMetadata;
|
|
644
644
|
discovery: DiscoveryRationale;
|
|
645
|
+
/**
|
|
646
|
+
* The criterion ids ABSORBED into this one by the near-duplicate MERGE
|
|
647
|
+
* (`scripts/merge-criteria.ts`). Provenance, not a pointer: the absorbed
|
|
648
|
+
* criterion's evidence lives on THIS criterion (refs unioned, prevalence
|
|
649
|
+
* recomputed over the union), so the merge is lossless and auditable. The
|
|
650
|
+
* FIRST-SEEN id survives; every absorbed id is listed here. Absent ⇒ nothing
|
|
651
|
+
* was merged into this criterion.
|
|
652
|
+
*/
|
|
653
|
+
mergedFrom?: string[];
|
|
654
|
+
/**
|
|
655
|
+
* Alternate ids that RESOLVE to this criterion — the absorbed ids kept live so
|
|
656
|
+
* an older report, a dataset recipe or a handoff that names the pre-merge id
|
|
657
|
+
* still finds its criterion (`resolveCriterionId`). A merge must never
|
|
658
|
+
* invalidate an outstanding reference.
|
|
659
|
+
*/
|
|
660
|
+
aliases?: string[];
|
|
645
661
|
/**
|
|
646
662
|
* The EXECUTABLE code-check reference (the uniform-standard companion of the
|
|
647
663
|
* human-readable `statement`). REQUIRED when `metadata.check_method` is
|
|
@@ -848,6 +864,10 @@ export const MinedCriterionSchema = Type.Object(
|
|
|
848
864
|
supportCount: Type.Integer({ minimum: 0 }),
|
|
849
865
|
metadata: MetricMetadataSchema,
|
|
850
866
|
discovery: DiscoveryRationaleSchema,
|
|
867
|
+
/** near-duplicate MERGE provenance — the absorbed ids (first-seen id survives). */
|
|
868
|
+
mergedFrom: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
869
|
+
/** alternate ids that still RESOLVE here (an absorbed id never goes dead). */
|
|
870
|
+
aliases: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
851
871
|
/** the executable code-check reference (uniform standard); OPTIONAL at the
|
|
852
872
|
* schema layer — `lint-uniformity.ts` enforces presence by check_method. */
|
|
853
873
|
codeEval: Type.Optional(CodeEvalSpecSchema),
|
|
@@ -987,12 +1007,18 @@ function locateByObs(obs: string, traces: EvalTrace[]): EvalTrace | undefined {
|
|
|
987
1007
|
|
|
988
1008
|
/**
|
|
989
1009
|
* Read the value at a dotted `path` (e.g. `output.response`, `observations.0.output`)
|
|
990
|
-
* out of a trace. Numeric segments index arrays.
|
|
991
|
-
*
|
|
992
|
-
*
|
|
1010
|
+
* out of a trace. Numeric segments index arrays. Bracket indices are TOLERATED
|
|
1011
|
+
* (`observations[3].output` ≡ `observations.3.output` — judges naturally emit the
|
|
1012
|
+
* bracket form; a syntax preference must never manufacture a dead ref). Returns
|
|
1013
|
+
* `undefined` when ANY segment is absent (no value at that path) — the caller
|
|
1014
|
+
* treats that as UNRESOLVED. An empty path reads the whole trace object. PURE.
|
|
993
1015
|
*/
|
|
994
1016
|
function readPath(root: unknown, path: string): unknown {
|
|
995
|
-
const segments = path
|
|
1017
|
+
const segments = path
|
|
1018
|
+
.replace(/\[(\d+)\]/g, ".$1")
|
|
1019
|
+
.split(".")
|
|
1020
|
+
.map((s) => s.trim())
|
|
1021
|
+
.filter((s) => s.length > 0);
|
|
996
1022
|
let cur: unknown = root;
|
|
997
1023
|
for (const seg of segments) {
|
|
998
1024
|
if (cur === null || cur === undefined) return undefined;
|