Files
gstack/scripts/judge-calibration-configs.ts
Garry Tanandcapy-ai[bot] 0c6398f8e6 v1.91.232.0 test(judge-calibration): cookie-workflow corpus and comparison-1 result, recorded as held (GSTA-91) (#3416)
* test(judge-calibration): preregistered cookie-workflow corpus, blind second review and adjudication; calibration config (GSTA-91)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(judge-calibration): price the cookie-workflow calibration before dispatch (GSTA-91)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(judge-calibration): record the cookie-workflow comparison-1 run (held: one new false pass) (GSTA-91)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(changelog): cookie-workflow calibration recorded as held (GSTA-91)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes (GSTA-91)

/ship Step 9 Fix-First on the cookie-workflow calibration record:
- README: scope the "no sample errored" sentence to the 2026-10-03 configurations,
  move the W2 pilot and W2 spend sentences back to the W2 paragraph, record the
  cookie UC3 prose-step trigger and that its hand-written manifest entry must
  survive manifest regeneration.
- TODOS: the cookie configuration now has a corpus and a held comparison-1; add
  its triggered prose step to the deferred prose-step entry.
- test: pin the cookie pass rule's actionability floor and validate() rejection,
  and guard that the cookie registration sends only COOKIE_WORKFLOW_JUDGE's keys
  so the calibrated request stays the request production sends.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes, pass 2 (GSTA-91)

/ship Step 9 Fix-First, second pass:
- README: the blind-review sentence and results heading now fit the cookie corpus
  (labels predate any request with the schema; the row carries its own date and
  model), and the cookie paragraph records that both valid old-a a07 samples
  also scored 4/4/4.
- CHANGELOG: same a07 fact.
- TODOS: today's cookie request false-passes a07 and a08; the deferred prose step
  targets those.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test(judge-calibration): pin the cookie calibration's prompt and recorded result (GSTA-91)

/ship Step 7 coverage audit:
- the cookie-workflow calibration builds exactly the prompt the cookie eval sends
  for the same bundle;
- analyze() reproduces the committed comparison-1 result from its labels,
  samples, stop record and request options (request_hash covers model and
  options only, so prompt text and bundle bytes are not pinned).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs(changelog): describe the new cookie calibration tests (GSTA-91)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-26 06:55:30 +00:00

118 lines
6.9 KiB
TypeScript

/**
* W2 calibration request configurations, keyed by distinct request (prompt
* builder, model, max_tokens, stream, effort, thresholds), not by test case.
* Each config builds prompts through the same builder the eval sends; "old" is
* today's request and "new" adds only the JSON schema (comparison 1).
*/
import {
ARM_JUDGE_MODEL, ARM_JUDGE_SCHEMA, buildArmJudgePrompt, parseArmJudgeResponse,
buildQaWorkflowJudgePrompt, buildQaHealthRubricJudgePrompt, buildQaAntiRefusalJudgePrompt,
buildCrossSkillConsistencyJudgePrompt, buildVoiceDirectiveJudgePrompt,
JUDGE_SCORE_SCHEMA, QA_ANTI_REFUSAL_JUDGE_SCHEMA, CROSS_SKILL_CONSISTENCY_JUDGE_SCHEMA,
VOICE_DIRECTIVE_JUDGE_SCHEMA, VOICE_DIRECTIVE_DIMENSIONS, JUDGE_SCORE_DIMENSIONS, DEFAULT_JUDGE_MAX_TOKENS,
judgePanelMedian, judgePanelMajority,
} from '../test/helpers/llm-judge';
import { buildWorkflowJudgePrompt, WORKFLOW_JUDGE_RESPONSE_SCHEMA } from '../test/helpers/workflow-judge-input';
import { COOKIE_WORKFLOW_JUDGE } from '../test/helpers/cookie-workflow-judge-input';
import { CLAUDE_JUDGE_EVAL_MODEL } from '../lib/eval-model';
import type { CalibrationConfig, Verdict } from './judge-calibration';
type Sample = Record<string, unknown>;
/** What production rejects: judgePanelMedian needs finite numbers, judgePanelMajority needs booleans. */
function requireFields(value: unknown, numbers: readonly string[], booleans: readonly string[] = []): void {
const sample = (value ?? {}) as Sample;
for (const key of numbers) {
if (typeof sample[key] !== 'number' || !Number.isFinite(sample[key])) throw new Error(`non-numeric ${key}: ${JSON.stringify(sample[key])}`);
}
for (const key of booleans) {
if (typeof sample[key] !== 'boolean') throw new Error(`non-boolean ${key}: ${JSON.stringify(sample[key])}`);
}
}
const meets = (median: Record<string, number>, floors: Record<string, number>): Verdict =>
Object.entries(floors).every(([key, floor]) => median[key]! >= floor) ? 'pass' : 'fail';
const QA_FLOORS = { clarity: 3, completeness: 3, actionability: 4 };
const scoreJudge = (floors: Record<string, number>) => ({
validate: (value: unknown) => requireFields(value, JUDGE_SCORE_DIMENSIONS),
verdict: (samples: unknown[]) => meets(judgePanelMedian<string>(samples as Sample[], JUDGE_SCORE_DIMENSIONS), floors),
});
const workflow = (id: string, priority: number, cases: string[], extra: { agentCapability?: 'frontier'; model?: string; floors?: Record<string, number> } = {}): CalibrationConfig => ({
id, priority, cases,
model: extra.model ?? CLAUDE_JUDGE_EVAL_MODEL,
build: inputs => buildWorkflowJudgePrompt({ judgeContext: inputs.judgeContext!, judgeGoal: inputs.judgeGoal!, agentCapability: extra.agentCapability },
{ files: [], text: inputs.bundle! }),
oldOptions: { max_tokens: DEFAULT_JUDGE_MAX_TOKENS },
newOptions: { max_tokens: DEFAULT_JUDGE_MAX_TOKENS, jsonSchema: WORKFLOW_JUDGE_RESPONSE_SCHEMA },
...scoreJudge(extra.floors ?? QA_FLOORS),
estimatedOutputTokens: 1200,
});
export const CALIBRATION_CONFIGS: CalibrationConfig[] = [
{
id: 'arm', priority: 1, cases: ['arm-benchmark (skill-e2e-arm-benchmark.test.ts)'], model: ARM_JUDGE_MODEL,
build: inputs => buildArmJudgePrompt(inputs.task!.trimEnd(), inputs.diff!),
oldOptions: {},
newOptions: { jsonSchema: ARM_JUDGE_SCHEMA },
validate: value => { parseArmJudgeResponse(value); },
verdict: samples => {
const scores = samples.map(sample => parseArmJudgeResponse(sample).over_engineering).sort((a, b) => a - b);
return scores[Math.floor(scores.length / 2)]! <= 1 ? 'pass' : 'fail';
},
estimatedOutputTokens: 300,
},
{
id: 'qa-workflow', priority: 2, cases: ['qa/SKILL.md workflow'], model: CLAUDE_JUDGE_EVAL_MODEL,
build: inputs => buildQaWorkflowJudgePrompt(inputs.section!),
oldOptions: {}, newOptions: { jsonSchema: JUDGE_SCORE_SCHEMA },
...scoreJudge(QA_FLOORS), estimatedOutputTokens: 1200,
},
{
id: 'qa-health-rubric', priority: 3, cases: ['qa/SKILL.md health rubric'], model: CLAUDE_JUDGE_EVAL_MODEL,
build: inputs => buildQaHealthRubricJudgePrompt(inputs.section!),
oldOptions: {}, newOptions: { jsonSchema: JUDGE_SCORE_SCHEMA },
...scoreJudge(QA_FLOORS), estimatedOutputTokens: 1200,
},
{
id: 'qa-anti-refusal', priority: 4, cases: ['qa/SKILL.md anti-refusal'], model: CLAUDE_JUDGE_EVAL_MODEL,
build: inputs => buildQaAntiRefusalJudgePrompt(inputs.diffAwareSection!, inputs.rulesSection!),
oldOptions: {}, newOptions: { jsonSchema: QA_ANTI_REFUSAL_JUDGE_SCHEMA },
validate: value => requireFields(value, ['confidence'], ['would_browse']),
verdict: samples => judgePanelMajority<string>(samples as Sample[], 'would_browse')
&& judgePanelMedian<string>(samples as Sample[], ['confidence']).confidence >= 4 ? 'pass' : 'fail',
estimatedOutputTokens: 1200,
},
{
id: 'cross-skill', priority: 5, cases: ['cross-skill greptile consistency'], model: CLAUDE_JUDGE_EVAL_MODEL,
build: inputs => buildCrossSkillConsistencyJudgePrompt(inputs.collected!),
oldOptions: {}, newOptions: { jsonSchema: CROSS_SKILL_CONSISTENCY_JUDGE_SCHEMA },
validate: value => requireFields(value, ['score'], ['consistent']),
verdict: samples => judgePanelMajority<string>(samples as Sample[], 'consistent')
&& judgePanelMedian<string>(samples as Sample[], ['score']).score >= 4 ? 'pass' : 'fail',
estimatedOutputTokens: 1200,
},
{
id: 'voice', priority: 6, cases: ['voice directive tone'], model: CLAUDE_JUDGE_EVAL_MODEL,
build: inputs => buildVoiceDirectiveJudgePrompt(inputs.voiceSection!),
oldOptions: {}, newOptions: { jsonSchema: VOICE_DIRECTIVE_JUDGE_SCHEMA },
validate: value => requireFields(value, VOICE_DIRECTIVE_DIMENSIONS),
verdict: samples => meets(judgePanelMedian<string>(samples as Sample[], VOICE_DIRECTIVE_DIMENSIONS),
Object.fromEntries(VOICE_DIRECTIVE_DIMENSIONS.map(key => [key, 4]))),
estimatedOutputTokens: 1200,
},
workflow('workflow-default', 7, ['document-release/SKILL.md workflow', 'plan-ceo-review/SKILL.md modes', 'plan-eng-review/SKILL.md sections',
'plan-design-review/SKILL.md passes', 'design-review/SKILL.md fix loop', 'design-consultation/SKILL.md research',
'land-and-deploy/SKILL.md workflow', 'canary/SKILL.md monitoring loop', 'benchmark/SKILL.md perf collection',
'setup-deploy/SKILL.md platform setup', 'sync-gbrain/SKILL.md read-only readiness', 'retro/SKILL.md instructions',
'qa-only/SKILL.md workflow', 'gstack-upgrade/SKILL.md upgrade flow']),
{
id: 'cookie-workflow', priority: 8, cases: ['setup-browser-cookies/SKILL.md workflow'], model: COOKIE_WORKFLOW_JUDGE.model,
build: inputs => buildWorkflowJudgePrompt(COOKIE_WORKFLOW_JUDGE, { files: [], text: inputs.bundle! }),
oldOptions: { max_tokens: DEFAULT_JUDGE_MAX_TOKENS },
newOptions: { max_tokens: DEFAULT_JUDGE_MAX_TOKENS, jsonSchema: WORKFLOW_JUDGE_RESPONSE_SCHEMA },
...scoreJudge(COOKIE_WORKFLOW_JUDGE.thresholds), estimatedOutputTokens: 1200,
},
];