@mutagent/evaluator 0.1.0-alpha.5 → 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 +28 -30
- package/.claude/skills/mutagent-evaluator/assets/agents/dataset-builder.md +6 -5
- package/.claude/skills/mutagent-evaluator/assets/agents/evaluator.md +93 -30
- package/.claude/skills/mutagent-evaluator/assets/code-quality-criteria.yaml +75 -0
- 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/lenses/methodology-critic-lens.md +1 -1
- package/.claude/skills/mutagent-evaluator/references/data-registry.md +43 -0
- package/.claude/skills/mutagent-evaluator/references/edd-loop.md +6 -6
- package/.claude/skills/mutagent-evaluator/references/eval-layers.md +52 -0
- package/.claude/skills/mutagent-evaluator/references/eval-stage.md +37 -4
- package/.claude/skills/mutagent-evaluator/references/memory-format.md +1 -1
- package/.claude/skills/mutagent-evaluator/schemas/methodology-review.schema.yaml +1 -1
- package/.claude/skills/mutagent-evaluator/scripts/agent-dispatch.ts +0 -0
- 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/build-review-ui.ts +320 -6
- package/.claude/skills/mutagent-evaluator/scripts/cli/prep.ts +32 -2
- package/.claude/skills/mutagent-evaluator/scripts/cli/profile-subject.ts +1 -1
- package/.claude/skills/mutagent-evaluator/scripts/code-eval-library.ts +201 -0
- package/.claude/skills/mutagent-evaluator/scripts/code-quality-verdict.ts +60 -68
- package/.claude/skills/mutagent-evaluator/scripts/config/schema.ts +3 -3
- package/.claude/skills/mutagent-evaluator/scripts/contracts/agentspec-evals.ts +101 -39
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-matrix.ts +326 -3
- package/.claude/skills/mutagent-evaluator/scripts/contracts/eval-types.ts +30 -4
- package/.claude/skills/mutagent-evaluator/scripts/determine-outcome.ts +8 -0
- package/.claude/skills/mutagent-evaluator/scripts/edd/change-request.ts +1 -1
- package/.claude/skills/mutagent-evaluator/scripts/edd/edd-types.ts +2 -2
- package/.claude/skills/mutagent-evaluator/scripts/judge-prompt-template.ts +144 -15
- 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/memory/append.ts +1 -1
- package/.claude/skills/mutagent-evaluator/scripts/memory/ratify.ts +165 -0
- package/.claude/skills/mutagent-evaluator/scripts/merge-criteria.ts +849 -0
- package/.claude/skills/mutagent-evaluator/scripts/prep-tasks.ts +15 -4
- package/.claude/skills/mutagent-evaluator/scripts/read-manifest.ts +120 -0
- package/.claude/skills/mutagent-evaluator/scripts/read-unitf-traces.ts +34 -2
- package/.claude/skills/mutagent-evaluator/scripts/render-build-cards.ts +37 -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 +11 -92
- package/.claude/skills/mutagent-evaluator/scripts/render-review-report-v3.ts +691 -0
- package/.claude/skills/mutagent-evaluator/scripts/report-fragments.ts +173 -0
- package/.claude/skills/mutagent-evaluator/scripts/route-failures.ts +12 -9
- package/.claude/skills/mutagent-evaluator/scripts/run-evaluate.ts +46 -1
- package/.claude/skills/mutagent-evaluator/scripts/run-pipeline.ts +39 -4
- package/.claude/skills/mutagent-evaluator/scripts/run-review.ts +266 -0
- package/.claude/skills/mutagent-evaluator/scripts/subject-profile.ts +82 -0
- package/.claude/skills/mutagent-evaluator/scripts/sync-eval-criteria.ts +2 -2
- package/.claude/skills/mutagent-evaluator/scripts/unitf-to-evaltrace.ts +64 -21
- package/.claude/skills/mutagent-evaluator/scripts/verify-render.ts +728 -0
- package/bin/mutagent-cli.mjs +9 -5
- package/package.json +2 -2
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* scripts/report-fragments.ts — EV-3 shared client-JS render fragments.
|
|
3
|
+
* ---------------------------------------------------------------------------
|
|
4
|
+
* The step<->criterion side-by-side render functions, extracted VERBATIM from
|
|
5
|
+
* `render-eval-report.ts` so BOTH the eval report AND the `*review` UI
|
|
6
|
+
* (`build-review-ui.ts`) emit the identical browser-side renderer.
|
|
7
|
+
*
|
|
8
|
+
* `SIDE_BY_SIDE_FRAGMENT_JS` is the BYTE-IDENTICAL block (`cvRefs` +
|
|
9
|
+
* `critiqueBlock` + `sideBySide` — the latter carrying its local
|
|
10
|
+
* `refExaminesStep` + `covEntry`). render-eval-report splices it into its
|
|
11
|
+
* client script IN PLACE, so the report's emitted HTML is unchanged (guarded by
|
|
12
|
+
* the render-eval-report byte-identity tests + a before/after HTML diff).
|
|
13
|
+
*
|
|
14
|
+
* The fragment is provider-of-record for the render logic; its runtime deps
|
|
15
|
+
* (`esc`, `resRouting`, `resBadge`, and the globals `C` / `RES`) are
|
|
16
|
+
* supplied by whichever host script embeds it (render-eval-report defines them
|
|
17
|
+
* above the splice; the review UI defines compatible copies — see
|
|
18
|
+
* `REVIEW_SIDE_BY_SIDE_DEPS_JS`).
|
|
19
|
+
*
|
|
20
|
+
* `STEP_ROW_FRAGMENT_JS` is the review-UI VIRTUALIZATION mirror: standalone
|
|
21
|
+
* `refExaminesStepR` / `covEntryR` / `stepRowR` that emit ONE step triplet
|
|
22
|
+
* with markup IDENTICAL to `sideBySide`'s per-step output — so the review UI
|
|
23
|
+
* can window a 1,686-step card without materializing the whole DOM. A drift
|
|
24
|
+
* guard (`tests/report-fragments.test.ts`) evaluates both paths and asserts the
|
|
25
|
+
* per-step HTML matches, so the mirror can never silently diverge from the
|
|
26
|
+
* frozen fragment.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* BYTE-IDENTICAL extraction of the eval report's `cvRefs` + `critiqueBlock` +
|
|
31
|
+
* `sideBySide` client functions (`sideBySide` contains its local
|
|
32
|
+
* `refExaminesStep` + `covEntry`). Spliced verbatim by render-eval-report and
|
|
33
|
+
* reused by the review UI. DO NOT EDIT — regenerate from render-eval-report if
|
|
34
|
+
* that block ever changes, and re-verify byte-identity.
|
|
35
|
+
*/
|
|
36
|
+
export const SIDE_BY_SIDE_FRAGMENT_JS = `function cvRefs(refs){if(!refs||!refs.length)return '';
|
|
37
|
+
return '<div class="cvrefs">'+refs.map(function(rf){var s=(typeof rf==='string')?rf:[rf.obs,rf.path,rf.value].filter(Boolean).join(' · ');return s?'<span class="cvref">'+esc(s)+'</span>':'';}).join('')+'</div>';}
|
|
38
|
+
function critiqueBlock(cvs){if(!cvs||!cvs.length)return '';
|
|
39
|
+
var order={fail:0,uncertain:1,indeterminate:1,pass:2,na:3};
|
|
40
|
+
var rows=cvs.slice().sort(function(a,b){return (order[a.result]==null?9:order[a.result])-(order[b.result]==null?9:order[b.result]);}).map(function(v){
|
|
41
|
+
var res=v.result||'na';var disp=res==='uncertain'?'indeterminate':res;
|
|
42
|
+
var conf=(v.confidence!=null)?'<span class="cvconf">conf '+esc(v.confidence)+(v.confidenceBand?' · '+esc(v.confidenceBand):'')+'</span>':'';
|
|
43
|
+
var crit=v.critique?'<div class="cvcrit">'+esc(v.critique)+'</div>':'<div class="cvcrit dim">— no critique recorded for this criterion</div>';
|
|
44
|
+
return '<div class="cvrow '+esc(res)+'"><div class="cvh"><span class="cvb '+esc(res)+'">'+esc(disp)+'</span><span class="cvid">'+esc(v.criterionId||'')+'</span>'+conf+'</div>'+crit+cvRefs(v.refs)+'</div>';}).join('');
|
|
45
|
+
return '<div class="ctx cvblock"><div class="ctx-h">◇ how the judge reasoned — per-criterion verdict · critique-before-verdict · grounding refs ('+cvs.length+')</div><div class="cvbody">'+rows+'</div></div>';}
|
|
46
|
+
function sideBySide(d){var ctx=d.context||{};
|
|
47
|
+
// Gap A — the §2 "input + scenario" cell renders the RAW triggering INPUT (the
|
|
48
|
+
// thing that fired the agent) ABOVE the judge's scenario LABEL. Long inputs
|
|
49
|
+
// collapse into a <details> (lean, no JS) so the cell stays compact; short ones
|
|
50
|
+
// render inline (clamped). ABSENT input ⇒ the cell shows the scenario alone (or
|
|
51
|
+
// "—"). Font stays at the --fs-2xs (11px) floor + brand mono.
|
|
52
|
+
var inputCell=function(raw,scen){
|
|
53
|
+
var sc=scen?'<div class="iscn">scenario · '+esc(scen)+'</div>':'';
|
|
54
|
+
if(!raw)return (scen?'<div class="ival">'+esc(scen)+'</div>':'—');
|
|
55
|
+
var long=String(raw).length>180;
|
|
56
|
+
var body=long
|
|
57
|
+
? '<details class="iexp"><summary>raw input · '+String(raw).length+' chars</summary><pre class="iraw">'+esc(raw)+'</pre></details>'
|
|
58
|
+
: '<pre class="iraw clamp">'+esc(raw)+'</pre>';
|
|
59
|
+
return body+sc;};
|
|
60
|
+
var refStr=function(rf){if(!rf)return '';if(typeof rf==='string')return rf;return [rf.obs,rf.path,rf.value].filter(Boolean).join(':');};
|
|
61
|
+
// UI-4 — surface the judge's REASONING (why the verdict + why the exit-states were
|
|
62
|
+
// concluded) from the EXISTING judge walk: an ordered why-chain band + the decide/bind
|
|
63
|
+
// text inlined where it explains a conclusion. No emit-contract change — read-only over
|
|
64
|
+
// the judge_steps already on the verdict file.
|
|
65
|
+
var KORD={gather:0,context:1,examine:2,detect:3,bind:4,ground:5,critique:6,decide:7,verify:8};
|
|
66
|
+
var allJs=(d.judgeSteps||[]).slice();
|
|
67
|
+
var stepText=function(kind){return allJs.filter(function(s){return s.kind===kind;}).map(function(s){return s.text||'';}).filter(Boolean).join(' · ');};
|
|
68
|
+
var decideWhy=stepText('decide')||stepText('critique');
|
|
69
|
+
var stateWhy=[stepText('bind'),stepText('gather')].filter(Boolean).join(' · ');
|
|
70
|
+
var chainSteps=allJs.slice().sort(function(a,b){var x=KORD[a.kind];var y=KORD[b.kind];return (x==null?9:x)-(y==null?9:y);});
|
|
71
|
+
var whyChain=chainSteps.length?('<div class="ctx whychain"><div class="ctx-h">◇ judge reasoning — full why-chain (gather → bind → ground → decide)</div><div class="wc-body">'+chainSteps.map(function(s){var rs=refStr(s.ref);return '<div class="jstep"><span class="k '+esc(s.kind)+'">'+esc(s.kind)+'</span><span class="t">'+esc(s.text||'')+(rs?' <span class=ref>'+esc(rs)+'</span>':'')+'</span></div>';}).join('')+'</div></div>'):'';
|
|
72
|
+
var verdictWhy=decideWhy?('<div class="band whyverdict"><div class="bh" style="color:var(--primary-soft)">◇ why this verdict</div><div class="wv-t">'+esc(decideWhy)+'</div></div>'):'';
|
|
73
|
+
// -- §2 judge lane = per-step EVAL COVERAGE --
|
|
74
|
+
// The judge lane (.step-r) used to filter judgeSteps by anchor === a.n -- but
|
|
75
|
+
// judges never emit an anchor, so EVERY step rendered a bare dash. Instead we
|
|
76
|
+
// map each agent step -> the per-criterion verdicts (d.criterionVerdicts) whose
|
|
77
|
+
// grounding refs EXAMINED that step: a precise ref.obs === step.obs match plus
|
|
78
|
+
// the tool-name fallback (ref.path === 'name' && ref.value === step.tool).
|
|
79
|
+
// Each examining criterion renders one compact entry -- result + CODE/JUDGE tag
|
|
80
|
+
// + criterionId + the judge reasoning (critique). A step no criterion references
|
|
81
|
+
// says 'not examined by any eval' (honest), never a bare dash. Any judge step
|
|
82
|
+
// that DOES carry a real anchor is still honored (future-proof).
|
|
83
|
+
var cvAll=(d.criterionVerdicts||[]);
|
|
84
|
+
var refExaminesStep=function(rf,a){
|
|
85
|
+
if(!rf||typeof rf==='string')return false;
|
|
86
|
+
if(a.obs&&rf.obs&&String(rf.obs)===String(a.obs))return true;
|
|
87
|
+
if(rf.path==='name'&&rf.value!=null&&a.tool&&String(rf.value)===String(a.tool))return true;
|
|
88
|
+
return false;};
|
|
89
|
+
var covEntry=function(v,a){
|
|
90
|
+
var res=v.result||'na';var disp=res==='uncertain'?'indeterminate':res;
|
|
91
|
+
// the router carries TWO vocabularies: matrix-derived ('deterministic') and
|
|
92
|
+
// mined ('code-based'); 'hybrid' is shared. A '[code-eval ...]'-prefixed critique
|
|
93
|
+
// is the deterministic-logic fallback when the method is unset.
|
|
94
|
+
var method=((typeof C!=='undefined'&&C[v.criterionId])||{}).m||'';
|
|
95
|
+
var isCode=method==='deterministic'||method==='code-based'||method==='hybrid'||String(v.critique||'').trim().indexOf('[code-eval')===0;
|
|
96
|
+
var tag=method==='hybrid'?'HYBRID':(isCode?'CODE':'JUDGE');
|
|
97
|
+
var matched=(v.refs||[]).filter(function(rf){return typeof rf!=='string'&&refExaminesStep(rf,a);});
|
|
98
|
+
var crit=v.critique?esc(v.critique):'<span class="dim">— no critique recorded</span>';
|
|
99
|
+
return '<div class="jcov '+esc(res)+'"><div class="jcov-h"><span class="cvb '+esc(res)+'">'+esc(disp)+'</span><span class="jm '+(isCode?'code':'judge')+'" title="'+(isCode?'deterministically checked by a code-eval (not LLM-judged)':'reasoned by the LLM judge')+'">'+esc(tag)+'</span><span class="jcid">'+esc(v.criterionId||'')+'</span></div><div class="jcrit">'+crit+'</div>'+cvRefs(matched)+'</div>';};
|
|
100
|
+
var rowsHtml='';(d.agentSteps||[]).forEach(function(a){
|
|
101
|
+
// future-proof: a judge step that carries a REAL anchor still renders.
|
|
102
|
+
var js=(d.judgeSteps||[]).filter(function(s){return s.anchor!=null&&String(s.anchor)===String(a.n)&&s.kind!=='context';});
|
|
103
|
+
var anchoredHtml=js.map(function(s){var rs=refStr(s.ref);return '<div class="jstep"><span class="k '+esc(s.kind)+'">'+esc(s.kind)+'</span><span class="t">'+esc(s.text||'')+' '+(rs?'<span class=ref>'+esc(rs)+'</span>':'')+'</span></div>';}).join('');
|
|
104
|
+
var examiners=cvAll.filter(function(v){return (v.refs||[]).some(function(rf){return refExaminesStep(rf,a);});});
|
|
105
|
+
var covHtml=examiners.map(function(v){return covEntry(v,a);}).join('');
|
|
106
|
+
var jhtml=anchoredHtml+covHtml;
|
|
107
|
+
if(!jhtml)jhtml='<div class="jstep noexam"><span class="t">— not examined by any eval</span></div>';
|
|
108
|
+
rowsHtml+='<div class="step-l"><div class="evb"><div class="top"><span class="tool">'+esc(a.tool||'')+'</span><span class="st '+esc(a.status||'')+'">'+esc(a.status||'')+'</span></div><div class="det">'+esc(a.detail||'')+'</div></div></div><div class="node '+esc(a.status||'')+'"><div class="ln"></div><div class="n">'+esc(a.n)+'</div><div class="ln"></div></div><div class="step-r"><div class="evb r">'+jhtml+'</div></div>';});
|
|
109
|
+
var h=d.health||{};
|
|
110
|
+
var sp=d.subjectProfile;
|
|
111
|
+
var spHtml=sp?'<div class="ctx"><div class="ctx-h">◇ judge · subject profile (M1) · '+esc(sp.provenance||'')+'</div><div class="ctx-g"><div class="ctx-c"><div class="l">identity</div><div class="v">'+esc(sp.identity||'—')+'</div></div><div class="ctx-c"><div class="l">purpose</div><div class="v">'+esc(sp.purpose||'—')+'</div></div><div class="ctx-c"><div class="l">scope · harness</div><div class="v">'+esc((sp.scope||'—'))+' · harness: '+esc(sp.harness||'—')+'</div></div></div></div>':'';
|
|
112
|
+
var u=d.understanding;
|
|
113
|
+
var uHtml=u?'<div class="band"><div class="bh" style="color:var(--cyan)">◇ node-0 GATHER — understanding (M2)</div><div class="re-rephrase" style="margin-top:4px">“'+esc(u.rephrase||'')+'”</div></div>':'';
|
|
114
|
+
var et=d.expectedTrajectory||[];
|
|
115
|
+
var etHtml=et.length?'<div class="band"><div class="bh" style="color:var(--primary-soft)">◇ node-0.5 EXPECTED-TRAJECTORY (M3) — how it SHOULD have acted</div><ol class="re-ul" style="margin-top:4px">'+et.map(function(s,i){return '<li><b>'+esc(s.step||i+1)+'.</b> '+esc(s.expected||'')+(s.rationale?' <span class=re-rat>— '+esc(s.rationale)+'</span>':'')+'</li>';}).join('')+'</ol></div>':'';
|
|
116
|
+
return '<div class="drillbox"><div style="display:flex;gap:9px;align-items:center;flex-wrap:wrap;margin-bottom:6px"><b class="mono">'+esc(d.traceId)+'</b><span class="chip">'+esc(d.route||'all')+'</span><span class="verd '+(d.verdict=='FAIL'?'fail':d.verdict=='PASS'?'pass':'inc')+'">'+esc(d.verdict)+'</span></div>'+
|
|
117
|
+
resRouting(d.res||'judge-walk')+
|
|
118
|
+
spHtml+
|
|
119
|
+
verdictWhy+
|
|
120
|
+
critiqueBlock(d.criterionVerdicts)+
|
|
121
|
+
'<div class="ctx"><div class="ctx-h">◇ judge · gather context</div><div class="ctx-g"><div class="ctx-c"><div class="l">harness</div><div class="v">'+esc(ctx.harness||'—')+'</div></div><div class="ctx-c"><div class="l">input + scenario</div><div class="v">'+inputCell(d.input,ctx.scenario)+'</div></div><div class="ctx-c"><div class="l">exit states</div><div class="v">'+esc(ctx.exitStates||'—')+(stateWhy?'<div class="why-note"><span class="ref">why concluded:</span> '+esc(stateWhy)+'</div>':'')+'</div></div></div></div>'+
|
|
122
|
+
whyChain+
|
|
123
|
+
uHtml+etHtml+
|
|
124
|
+
'<div class="lanehdr"><div class="a">target agent — what it did</div><div class="x">step</div><div class="j">judge — eval coverage (which criteria examined this step)</div></div>'+
|
|
125
|
+
'<div class="grid2">'+rowsHtml+'<div class="band loc"><div class="bh">↯ localize (root, not symptom)</div><div style="font-size:var(--fs-sm);margin-top:4px">'+esc(d.localize||'—')+'</div></div></div>'+
|
|
126
|
+
'<div class="health"><div class="hc"><div class="l">context</div><div class="v good">'+(h.contextGathered?'✓':'—')+'</div></div><div class="hc"><div class="l">grounded</div><div class="v good">'+esc(h.grounded||0)+'</div></div><div class="hc"><div class="l">assumed</div><div class="v '+((h.assumed||0)>0?'warn':'good')+'">'+esc(h.assumed||0)+'</div></div><div class="hc"><div class="l">root vs symptom</div><div class="v good">'+(h.stoppedAtSymptom?'symptom':'✓ root')+'</div></div></div></div>';}`;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The runtime deps the extracted `sideBySide` needs — `esc` / `resBadge` /
|
|
130
|
+
* `resRouting` — copied BYTE-IDENTICALLY from render-eval-report's client script
|
|
131
|
+
* so the reused `sideBySide` behaves identically wherever it is embedded. The
|
|
132
|
+
* review UI embeds these (render-eval-report already defines them above its own
|
|
133
|
+
* splice). `RES` may be an empty map in the review UI — `resRouting`/`resBadge`
|
|
134
|
+
* degrade to '' when a resolution key is absent.
|
|
135
|
+
*/
|
|
136
|
+
export const REVIEW_SIDE_BY_SIDE_DEPS_JS = `function esc(s){return String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');}
|
|
137
|
+
function resBadge(res){var m=RES[res];if(!m)return '';return '<span class="resbadge '+m.cls+'" title="'+esc(m.routing)+'">'+esc(m.badge)+'</span>';}
|
|
138
|
+
function resRouting(res){var m=RES[res];if(!m)return '';return '<div class="routing '+m.cls+'"><span class="routing-k">resolution</span><span class="routing-b '+m.cls+'">'+esc(m.badge)+'</span><span class="routing-v">'+esc(m.routing)+'</span></div>';}`;
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* The review-UI VIRTUALIZATION mirror. `stepRowR` emits ONE step triplet
|
|
142
|
+
* (`.step-l` + `.node` + `.step-r`) whose markup is CHARACTER-IDENTICAL to the
|
|
143
|
+
* per-step output of the frozen `sideBySide` (compare render-eval-report's
|
|
144
|
+
* `rowsHtml` loop). This lets the review UI window a 1,686-step card — rendering
|
|
145
|
+
* only the visible slice — instead of materializing the whole DOM (the EV-3
|
|
146
|
+
* wall). `refExaminesStepR` / `covEntryR` mirror `sideBySide`'s local
|
|
147
|
+
* `refExaminesStep` / `covEntry`. Depends on `esc` + `cvRefs` (from the deps +
|
|
148
|
+
* the frozen fragment). The drift guard in `tests/report-fragments.test.ts`
|
|
149
|
+
* evaluates both paths and asserts equality, so this mirror can never diverge.
|
|
150
|
+
*/
|
|
151
|
+
export const STEP_ROW_FRAGMENT_JS = `function refStrR(rf){if(!rf)return '';if(typeof rf==='string')return rf;return [rf.obs,rf.path,rf.value].filter(Boolean).join(':');}
|
|
152
|
+
function refExaminesStepR(rf,a){
|
|
153
|
+
if(!rf||typeof rf==='string')return false;
|
|
154
|
+
if(a.obs&&rf.obs&&String(rf.obs)===String(a.obs))return true;
|
|
155
|
+
if(rf.path==='name'&&rf.value!=null&&a.tool&&String(rf.value)===String(a.tool))return true;
|
|
156
|
+
return false;}
|
|
157
|
+
function covEntryR(v,a){
|
|
158
|
+
var res=v.result||'na';var disp=res==='uncertain'?'indeterminate':res;
|
|
159
|
+
var method=((typeof C!=='undefined'&&C&&C[v.criterionId])||{}).m||'';
|
|
160
|
+
var isCode=method==='deterministic'||method==='code-based'||method==='hybrid'||String(v.critique||'').trim().indexOf('[code-eval')===0;
|
|
161
|
+
var tag=method==='hybrid'?'HYBRID':(isCode?'CODE':'JUDGE');
|
|
162
|
+
var matched=(v.refs||[]).filter(function(rf){return typeof rf!=='string'&&refExaminesStepR(rf,a);});
|
|
163
|
+
var crit=v.critique?esc(v.critique):'<span class="dim">— no critique recorded</span>';
|
|
164
|
+
return '<div class="jcov '+esc(res)+'"><div class="jcov-h"><span class="cvb '+esc(res)+'">'+esc(disp)+'</span><span class="jm '+(isCode?'code':'judge')+'" title="'+(isCode?'deterministically checked by a code-eval (not LLM-judged)':'reasoned by the LLM judge')+'">'+esc(tag)+'</span><span class="jcid">'+esc(v.criterionId||'')+'</span></div><div class="jcrit">'+crit+'</div>'+cvRefs(matched)+'</div>';}
|
|
165
|
+
function stepRowInnerR(a,cvAll,judgeSteps){
|
|
166
|
+
var js=(judgeSteps||[]).filter(function(s){return s.anchor!=null&&String(s.anchor)===String(a.n)&&s.kind!=='context';});
|
|
167
|
+
var anchoredHtml=js.map(function(s){var rs=refStrR(s.ref);return '<div class="jstep"><span class="k '+esc(s.kind)+'">'+esc(s.kind)+'</span><span class="t">'+esc(s.text||'')+' '+(rs?'<span class=ref>'+esc(rs)+'</span>':'')+'</span></div>';}).join('');
|
|
168
|
+
var examiners=(cvAll||[]).filter(function(v){return (v.refs||[]).some(function(rf){return refExaminesStepR(rf,a);});});
|
|
169
|
+
var covHtml=examiners.map(function(v){return covEntryR(v,a);}).join('');
|
|
170
|
+
var jhtml=anchoredHtml+covHtml;
|
|
171
|
+
if(!jhtml)jhtml='<div class="jstep noexam"><span class="t">— not examined by any eval</span></div>';
|
|
172
|
+
return '<div class="step-l"><div class="evb"><div class="top"><span class="tool">'+esc(a.tool||'')+'</span><span class="st '+esc(a.status||'')+'">'+esc(a.status||'')+'</span></div><div class="det">'+esc(a.detail||'')+'</div></div></div><div class="node '+esc(a.status||'')+'"><div class="ln"></div><div class="n">'+esc(a.n)+'</div><div class="ln"></div></div><div class="step-r"><div class="evb r">'+jhtml+'</div></div>';}
|
|
173
|
+
function stepExaminedByR(a,cvAll){return (cvAll||[]).some(function(v){return (v.refs||[]).some(function(rf){return refExaminesStepR(rf,a);});});}`;
|
|
@@ -35,18 +35,21 @@ export const AdlStage = {
|
|
|
35
35
|
Build: "build",
|
|
36
36
|
Evaluate: "evaluate",
|
|
37
37
|
Diagnose: "diagnose",
|
|
38
|
-
|
|
38
|
+
Optimize: "optimize",
|
|
39
39
|
Audit: "audit",
|
|
40
|
+
// ⑥ SHIP (#1202) — mirrors the orchestrator handover-contract AdlStage. The evaluator
|
|
41
|
+
// never emits a ship handover (route-failures always routes to diagnose), but the mirror
|
|
42
|
+
// stays in lock-step with the canonical enum by design.
|
|
43
|
+
Ship: "ship",
|
|
40
44
|
} as const;
|
|
41
45
|
export type AdlStageValue = (typeof AdlStage)[keyof typeof AdlStage];
|
|
42
46
|
|
|
43
47
|
// Mirrors the orchestrator handover-contract SubjectKind. The CANONICAL inter-stage
|
|
44
|
-
// subject vocabulary is `agent | skill
|
|
45
|
-
//
|
|
46
|
-
//
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
export const SubjectKind = { Skill: "skill", Agent: "agent", Code: "code" } as const;
|
|
48
|
+
// subject vocabulary is `agent | skill` — how the subject is REALIZED (its substrate)
|
|
49
|
+
// is a separate axis, NOT conflated into `kind`. The #1202 / FU-69 §1.7 / ORCH-08 cleanup
|
|
50
|
+
// DROPPED `code` from this union in lock-step with the orchestrator handover-contract (the
|
|
51
|
+
// evaluator re-implements the shape, never imports it).
|
|
52
|
+
export const SubjectKind = { Skill: "skill", Agent: "agent" } as const;
|
|
50
53
|
export type SubjectKindValue = (typeof SubjectKind)[keyof typeof SubjectKind];
|
|
51
54
|
|
|
52
55
|
export const ArtifactKind = {
|
|
@@ -76,7 +79,6 @@ const SubjectSchema = Type.Object(
|
|
|
76
79
|
kind: Type.Union([
|
|
77
80
|
Type.Literal(SubjectKind.Skill),
|
|
78
81
|
Type.Literal(SubjectKind.Agent),
|
|
79
|
-
Type.Literal(SubjectKind.Code),
|
|
80
82
|
]),
|
|
81
83
|
name: Type.String({ minLength: 1 }),
|
|
82
84
|
path: Type.String({ minLength: 1 }),
|
|
@@ -142,8 +144,9 @@ const HandoverBundleSchema = Type.Object(
|
|
|
142
144
|
Type.Literal(AdlStage.Build),
|
|
143
145
|
Type.Literal(AdlStage.Evaluate),
|
|
144
146
|
Type.Literal(AdlStage.Diagnose),
|
|
145
|
-
Type.Literal(AdlStage.
|
|
147
|
+
Type.Literal(AdlStage.Optimize),
|
|
146
148
|
Type.Literal(AdlStage.Audit),
|
|
149
|
+
Type.Literal(AdlStage.Ship),
|
|
147
150
|
]),
|
|
148
151
|
subject: SubjectSchema,
|
|
149
152
|
intent: IntentSchema,
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
renderEvalReport,
|
|
57
57
|
} from "./render-eval-report.ts";
|
|
58
58
|
import { maskedCanonicalJson } from "./mask.ts";
|
|
59
|
+
import { renderEvalReportV3 } from "./render-eval-report-v3.ts";
|
|
59
60
|
import {
|
|
60
61
|
calibrationItems,
|
|
61
62
|
routeFailures,
|
|
@@ -95,6 +96,12 @@ export interface EvaluateRunInput {
|
|
|
95
96
|
* ABSENT ⇒ byte-stable legacy packets (the judge reconstructs at reason-time).
|
|
96
97
|
*/
|
|
97
98
|
subjectProfile?: SubjectProfile;
|
|
99
|
+
/**
|
|
100
|
+
* v3 D1=B+ — the pipeable `*evaluate-<layer>` filter carried into every packet:
|
|
101
|
+
* ABSENT/empty ⇒ the FULL layered walk (byte-stable legacy packets). Values are
|
|
102
|
+
* layer ids (L0..L4); the judge runs ONLY those phases — criteria still verdicted.
|
|
103
|
+
*/
|
|
104
|
+
layerScope?: string[];
|
|
98
105
|
pin: PinnedEnvelope;
|
|
99
106
|
/** where PREP writes packet files. */
|
|
100
107
|
packetDir: string;
|
|
@@ -291,6 +298,7 @@ export function prepMatrixPackets(input: EvaluateRunInput): string[] {
|
|
|
291
298
|
entry.residualCriteria,
|
|
292
299
|
input.pin,
|
|
293
300
|
input.subjectProfile,
|
|
301
|
+
input.layerScope,
|
|
294
302
|
);
|
|
295
303
|
ids.push(writeMatrixPacket(input.packetDir, packet));
|
|
296
304
|
}
|
|
@@ -593,7 +601,44 @@ export function writeRunReport(
|
|
|
593
601
|
const dir = reportDir(runId, cwd);
|
|
594
602
|
mkdirSync(dir, { recursive: true });
|
|
595
603
|
const outPath = join(dir, "evaluation-report.html");
|
|
596
|
-
|
|
604
|
+
// W3 — the production report is the FROZEN-CONTRACT template render (v3). The v2
|
|
605
|
+
// renderer stays available (evaluation-report.v2.html) as the transition fallback
|
|
606
|
+
// so nothing the operator relied on disappears in one step.
|
|
607
|
+
const gate = result.scorecard.gate as unknown as {
|
|
608
|
+
passed?: boolean;
|
|
609
|
+
runVerdict?: string;
|
|
610
|
+
failedCriteria?: { criterionId: string; severity?: string }[];
|
|
611
|
+
gatedBy?: { criterionId: string; severity?: string }[];
|
|
612
|
+
indeterminateBy?: { criterionId: string; severity?: string }[];
|
|
613
|
+
};
|
|
614
|
+
writeFileSync(
|
|
615
|
+
outPath,
|
|
616
|
+
renderEvalReportV3({
|
|
617
|
+
subjectName: input.subject.name,
|
|
618
|
+
runId,
|
|
619
|
+
audience: audience === "internal" ? "internal" : "external",
|
|
620
|
+
criteria: input.criteria,
|
|
621
|
+
files: matrixVerdictFiles,
|
|
622
|
+
...(input.subjectProfile !== undefined ? { subjectProfile: input.subjectProfile as unknown as Record<string, unknown> } : {}),
|
|
623
|
+
pin: input.pin,
|
|
624
|
+
gate: {
|
|
625
|
+
passed: gate.passed ?? false,
|
|
626
|
+
runVerdict: gate.runVerdict ?? "fail",
|
|
627
|
+
failedCriteria: gate.failedCriteria ?? [],
|
|
628
|
+
...(gate.gatedBy !== undefined ? { gatedBy: gate.gatedBy } : {}),
|
|
629
|
+
...(gate.indeterminateBy !== undefined ? { indeterminateBy: gate.indeterminateBy } : {}),
|
|
630
|
+
},
|
|
631
|
+
groundedPct: result.groundingReadiness.groundedPctOfDecided,
|
|
632
|
+
traces: input.trajectories as unknown as { id: string }[],
|
|
633
|
+
generatedAt: input.producedAt,
|
|
634
|
+
independentVerify: result.independentVerify.map((r) => ({
|
|
635
|
+
criterionId: r.criterionId ?? "?",
|
|
636
|
+
upheld: r.upheld,
|
|
637
|
+
reason: r.reason,
|
|
638
|
+
})),
|
|
639
|
+
}),
|
|
640
|
+
);
|
|
641
|
+
writeFileSync(join(dir, "evaluation-report.v2.html"), renderEvalReport(reportInput));
|
|
597
642
|
return outPath;
|
|
598
643
|
}
|
|
599
644
|
|
|
@@ -22,6 +22,12 @@
|
|
|
22
22
|
import type { JudgeInvoke } from "./determine-outcome.ts";
|
|
23
23
|
import { determineOutcome, runJudge } from "./judge-prompt-template.ts";
|
|
24
24
|
import { profileSubject, type SubjectProfile } from "./profile-subject.ts";
|
|
25
|
+
import { buildSubjectProfile, deriveSubjectFrame } from "./subject-profile.ts";
|
|
26
|
+
import {
|
|
27
|
+
normativeCriterionIds,
|
|
28
|
+
pendingRatifications,
|
|
29
|
+
type CalibrationDecision,
|
|
30
|
+
} from "./memory/ratify.ts";
|
|
25
31
|
import { splitTrainEval, buildJudgeSpec, type JudgePin } from "./build-evals.ts";
|
|
26
32
|
import { rollupScorecard, type GradedCriterion, type Scorecard } from "./evaluate.ts";
|
|
27
33
|
import {
|
|
@@ -64,6 +70,12 @@ export interface PipelineOptions {
|
|
|
64
70
|
severityById?: Record<string, string>;
|
|
65
71
|
/** artifact refs to enumerate on the handoff (caller-injected locators). */
|
|
66
72
|
artifacts?: ArtifactRef[];
|
|
73
|
+
/**
|
|
74
|
+
* EV-4 — prior operator ratify/eliminate decisions (the review UI's `calibration.json`,
|
|
75
|
+
* routed via `memory/ratify.ts`). A NORMATIVE criterion with no `verify` here is
|
|
76
|
+
* `needs-ratification` → the verdict is HELD (surfaced in `pendingRatification`).
|
|
77
|
+
*/
|
|
78
|
+
ratifications?: CalibrationDecision[];
|
|
67
79
|
}
|
|
68
80
|
|
|
69
81
|
export interface PipelineResult {
|
|
@@ -73,6 +85,12 @@ export interface PipelineResult {
|
|
|
73
85
|
verdicts: CriterionVerdict[];
|
|
74
86
|
scorecard: Scorecard;
|
|
75
87
|
handoff: HandoverBundle;
|
|
88
|
+
/**
|
|
89
|
+
* EV-4 — normative criteria still `needs-ratification` (no operator `verify`).
|
|
90
|
+
* NON-EMPTY ⇒ the verdict is HELD: a value call the judge cannot settle is
|
|
91
|
+
* unresolved, so the run's verdict is not yet final.
|
|
92
|
+
*/
|
|
93
|
+
pendingRatification: string[];
|
|
76
94
|
}
|
|
77
95
|
|
|
78
96
|
/** Map a determiner outcome → a sampling LabeledTrace. */
|
|
@@ -101,11 +119,20 @@ export async function runEvalPipeline(
|
|
|
101
119
|
// EV-049 — subject profile (carries the subject vocab the engine reads).
|
|
102
120
|
const profile = profileSubject(traces, opts.vocab);
|
|
103
121
|
|
|
122
|
+
// EV-5 CUT WIRE 2 — build the M1 judge-packet profile ONCE (identity · purpose ·
|
|
123
|
+
// tools + reversibility). This is `buildSubjectProfile`'s first PRODUCTION caller;
|
|
124
|
+
// it flows to the per-criterion judge preamble (EV-5). The determiner (EV-1) reads
|
|
125
|
+
// the SAME frame via `deriveSubjectFrame` so PREP (Stage A) and this pipeline
|
|
126
|
+
// (Stage B) render byte-identical determiner prompts (cross-stage cache parity).
|
|
127
|
+
const m1Profile = buildSubjectProfile({ subjectName: opts.subject.name, traces });
|
|
128
|
+
const subjectFrame = deriveSubjectFrame(opts.subject.name, traces);
|
|
129
|
+
|
|
104
130
|
// EV-042 — determine an outcome per trace (sequential → deterministic order).
|
|
105
|
-
// The determiner reads the subject vocab off the profile (EV-002)
|
|
131
|
+
// The determiner reads the subject vocab off the profile (EV-002) and, EV-1, the
|
|
132
|
+
// subject frame so it derives THIS subject's expected outcome (not email signals).
|
|
106
133
|
const outcomes: OutcomeResult[] = [];
|
|
107
134
|
for (const t of traces) {
|
|
108
|
-
outcomes.push(await determineOutcome(t, judge, profile.vocab));
|
|
135
|
+
outcomes.push(await determineOutcome(t, judge, profile.vocab, subjectFrame));
|
|
109
136
|
}
|
|
110
137
|
const labeled = traces.map((t, i) => toLabeled(t, outcomes[i]));
|
|
111
138
|
|
|
@@ -125,7 +152,7 @@ export async function runEvalPipeline(
|
|
|
125
152
|
const spec = buildJudgeSpec(criterion, exemplarsFromTrain(train), trainIds);
|
|
126
153
|
// judge a representative held-out subject trace (first eval target).
|
|
127
154
|
const subjectTrace = judgeTargets[0]?.trace ?? sample[0]?.trace ?? traces[0];
|
|
128
|
-
const verdict = await runJudge(spec, subjectTrace, judge, opts.pin);
|
|
155
|
+
const verdict = await runJudge(spec, subjectTrace, judge, opts.pin, m1Profile);
|
|
129
156
|
verdicts.push(verdict);
|
|
130
157
|
graded.push({
|
|
131
158
|
criterionId: criterion.id,
|
|
@@ -171,5 +198,13 @@ export async function runEvalPipeline(
|
|
|
171
198
|
producedAt: opts.producedAt,
|
|
172
199
|
});
|
|
173
200
|
|
|
174
|
-
|
|
201
|
+
// EV-4 — a normative criterion (a value/standard call the judge cannot settle)
|
|
202
|
+
// must be operator-RATIFIED before its verdict is final. Compute which normative
|
|
203
|
+
// criteria have no `verify` decision → the verdict is HELD on them.
|
|
204
|
+
const pendingRatification = pendingRatifications(
|
|
205
|
+
normativeCriterionIds(verdicts),
|
|
206
|
+
opts.ratifications ?? [],
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
return { profile, outcomes, sample, verdicts, scorecard, handoff, pendingRatification };
|
|
175
210
|
}
|