feat: add regression arm and two verdicts (task 0005)
Turn a benchmark run into a three-arm experiment: alongside the new-skill and no-skill arms, add a previous-version arm materialized from the default branch's HEAD, drawn automatically whenever the skill's directory differs from HEAD and degrading to the two-arm efficacy-only shape otherwise. Two blind head-to-heads now fall out per trial — efficacy (new-vs-no-skill) and regression (new-vs-old). The deterministic core dispatches a pass rule per comparison (efficacy at wins>=3 and losses<=1, regression at losses<=1 with no wins floor), gates only Efficacy on the hard assertion, and yields a second skill-level Regression verdict, green when no case regressed and not-applicable on a two-arm run. The report gains a second badge, a headline reading both verdicts together, a previous-version cost row with a two-ratio footnote, and per-case side-by-side comparisons that auto-expand on any failure or flagged loss to show the losing-trial output pair. The fixture test covers both shapes in one test: a three-arm run asserting per-arm metrics, both per-case and skill-level verdicts, the net margins, the three-row cost table and losing-trial evidence, and the two-arm run retained as the degenerate no-previous-version case.
This commit was merged in pull request #5.
This commit is contained in:
515
checks/fixtures/benchmark/two-arm-bundle.json
Normal file
515
checks/fixtures/benchmark/two-arm-bundle.json
Normal file
@@ -0,0 +1,515 @@
|
||||
{
|
||||
"skill": "sample-skill-2arm",
|
||||
"generatedAt": "2026-07-24T12:00:00Z",
|
||||
"temperature": 1.0,
|
||||
"trialsPerCase": 5,
|
||||
"arms": [
|
||||
{
|
||||
"id": "new",
|
||||
"label": "New skill"
|
||||
},
|
||||
{
|
||||
"id": "baseline",
|
||||
"label": "No skill"
|
||||
}
|
||||
],
|
||||
"comparisons": [
|
||||
{
|
||||
"id": "efficacy",
|
||||
"label": "Efficacy",
|
||||
"new": "new",
|
||||
"against": "baseline",
|
||||
"rule": "efficacy"
|
||||
}
|
||||
],
|
||||
"cases": [
|
||||
{
|
||||
"name": "clean-pass",
|
||||
"description": "New skill reliably beats the no-skill baseline.",
|
||||
"softCriteria": [
|
||||
"The answer is grounded in the fixture.",
|
||||
"The answer follows the skill's format."
|
||||
],
|
||||
"trials": [
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-clean-pass-t0",
|
||||
"baseline": "OUT-baseline-clean-pass-t0"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-clean-pass-t1",
|
||||
"baseline": "OUT-baseline-clean-pass-t1"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-clean-pass-t2",
|
||||
"baseline": "OUT-baseline-clean-pass-t2"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-clean-pass-t3",
|
||||
"baseline": "OUT-baseline-clean-pass-t3"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "tie",
|
||||
"rationale": "judge: efficacy tie"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-clean-pass-t4",
|
||||
"baseline": "OUT-baseline-clean-pass-t4"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "regresses-baseline",
|
||||
"description": "New skill does not reliably beat the baseline.",
|
||||
"softCriteria": [
|
||||
"The answer resolves the user's request."
|
||||
],
|
||||
"trials": [
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-regresses-baseline-t0",
|
||||
"baseline": "OUT-baseline-regresses-baseline-t0"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-regresses-baseline-t1",
|
||||
"baseline": "OUT-baseline-regresses-baseline-t1"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "tie",
|
||||
"rationale": "judge: efficacy tie"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-regresses-baseline-t2",
|
||||
"baseline": "OUT-baseline-regresses-baseline-t2"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "baseline",
|
||||
"rationale": "judge: efficacy baseline"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-regresses-baseline-t3",
|
||||
"baseline": "OUT-baseline-regresses-baseline-t3"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "baseline",
|
||||
"rationale": "judge: efficacy baseline"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-regresses-baseline-t4",
|
||||
"baseline": "OUT-baseline-regresses-baseline-t4"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "hard-gate-fail",
|
||||
"description": "Output wins the head-to-head but violates a hard assertion.",
|
||||
"softCriteria": [
|
||||
"The output is correct."
|
||||
],
|
||||
"trials": [
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-hard-gate-fail-t0",
|
||||
"baseline": "OUT-baseline-hard-gate-fail-t0"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-hard-gate-fail-t1",
|
||||
"baseline": "OUT-baseline-hard-gate-fail-t1"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-hard-gate-fail-t2",
|
||||
"baseline": "OUT-baseline-hard-gate-fail-t2"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-hard-gate-fail-t3",
|
||||
"baseline": "OUT-baseline-hard-gate-fail-t3"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"comparisons": {
|
||||
"efficacy": {
|
||||
"winner": "new",
|
||||
"rationale": "judge: efficacy new"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"new": "OUT-new-hard-gate-fail-t4",
|
||||
"baseline": "OUT-baseline-hard-gate-fail-t4"
|
||||
},
|
||||
"usage": {
|
||||
"new": {
|
||||
"input": 1000,
|
||||
"output": 2000,
|
||||
"cacheCreation": 400,
|
||||
"cacheRead": 8000,
|
||||
"turns": 3
|
||||
},
|
||||
"baseline": {
|
||||
"input": 500,
|
||||
"output": 1000,
|
||||
"cacheCreation": 200,
|
||||
"cacheRead": 4000,
|
||||
"turns": 2
|
||||
}
|
||||
},
|
||||
"hard": {
|
||||
"ran": true,
|
||||
"pass": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user