#!/usr/bin/env tsx /** * GEPA Faza 1 — Gen 1 evaluation runner. * * Per launch decision §G step 7+ + manifest v7 §gepa + Amendment 5. * * For each of 5 shapes, evaluate 3 candidates (baseline + 2 mutations) × 8 * instances = 120 total evaluations. Same instances as NULL-baseline (seed=42) * for direct shape-vs-shape comparison. * * Halt at: * - 30 evaluations (Checkpoint B per launch decision §E) * - $26 cumulative (cost halt per Amendment 3 + super-linear) * - 2 consecutive cell-semantic violations (per brief §5) * * Mode: MULTI-STEP (retrieval available) per Amendment 5 Ask 1 ratification. */ import * as fs from 'node:fs'; import * as path from 'node:path'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { MindDB, FrameStore, SessionStore, HybridSearch, createOllamaEmbedder, type Embedder, } from '@waggle/core'; import { runRetrievalAgentLoop, type LlmCallFn, type LlmCallInput, type LlmCallResult as AgentLlmCallResult, type RetrievalSearchFn, type AgentRunResult, // Amendment 8 §canonical_mutation_api: REGISTRY + registerShape MUST be imported from // '@waggle/agent' (same path the agent-loop uses internally). Importing via deep // relative paths produces a separate module instance under tsx + Node ESM workspace // resolution → mutations would not propagate. Diagnostic probe + Gen 1 partial // b5avslp51 confirmed empirically. REGISTRY, registerShape, type PromptShape, } from '@waggle/agent'; import { type CorpusInstance } from '../../src/faza-1/corpus.js'; import { NULL_BASELINE_PER_SHAPE, NULL_BASELINE_AGGREGATE, type DeltaFloorVerdict, type TieredFitnessComponents, } from '../../src/faza-1/types.js'; import { computeTieredFitness, computeDeltaFloorVerdict, computeTier2RetrievalBonus, } from '../../src/faza-1/fitness.js'; import { validateCandidate, type ValidatorVerdict, } from '../../src/faza-1/mutation-validator.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const REPO_ROOT = path.resolve(__dirname, '../../../..'); const CORPUS_JSONL = path.join(REPO_ROOT, 'benchmarks/results/gepa-faza1/corpus/h3-northlane-cfo-50-instances.jsonl'); const PROMPT_SHAPES_DIR = path.join(REPO_ROOT, 'packages/agent/src/prompt-shapes'); const GEPA_EVOLVED_DIR = path.join(PROMPT_SHAPES_DIR, 'gepa-evolved'); const OUT_DIR = path.join(REPO_ROOT, 'benchmarks/results/gepa-faza1/gen-1'); const OUT_JSONL = path.join(OUT_DIR, 'gen-1-eval.jsonl'); const RUN_LOG = path.join(OUT_DIR, 'gen-1-run.log'); const SUMMARY_JSON = path.join(OUT_DIR, 'gen-1-summary.json'); const SCRATCH_DIR = path.join(REPO_ROOT, 'tmp/gepa-faza1-gen-1'); const LITELLM_URL = process.env.LITELLM_URL ?? 'http://localhost:4000'; const OLLAMA_URL = 'http://localhost:11434'; const EMBEDDER_MODEL = 'nomic-embed-text'; const SAMPLING_SEED = 42; const N_PER_SHAPE = 8; const N_CANDIDATES_PER_SHAPE = 3; // 1 baseline + 2 mutations const SHAPES = ['claude', 'qwen-thinking', 'qwen-non-thinking', 'gpt', 'generic-simple'] as const; type ShapeName = typeof SHAPES[number]; const SUBJECT_ALIAS = 'qwen3.6-35b-a3b-via-dashscope-direct'; const SUBJECT_MAX_TOKENS = 16000; const SUBJECT_THINKING = true; const JUDGES = ['claude-opus-4-7', 'gpt-5.4', 'minimax-m27-via-openrouter'] as const; const JUDGE_MAX_TOKENS = 3000; const JUDGE_RETRIES = 3; const MAX_STEPS = 5; const MAX_RETRIEVALS_PER_STEP = 8; const PER_CALL_HALT_USD = 0.40; const PER_CELL_HALT_USD = 1.00; // Halt at 30 evals (Checkpoint B) unless --full const CHECKPOINT_B_HALT_EVALS = 30; const COST_HALT_USD = 26.0; // 30% over $20 NULL projection (Amendment 3 envelope basis) const MODEL_PRICING: Record = { 'claude-opus-4-7': { in: 15.0, out: 75.0 }, 'gpt-5.4': { in: 2.5, out: 10.0 }, 'minimax-m27-via-openrouter': { in: 0.7, out: 2.8 }, 'qwen3.6-35b-a3b-via-dashscope-direct': { in: 0.20, out: 0.80 }, 'qwen3.6-35b-a3b-via-openrouter': { in: 0.6, out: 2.4 }, }; const MANIFEST_ANCHOR = 'manifest-v7-gepa-faza1'; const MANIFEST_SHA_AMENDMENT_5 = '062dfc4935aaa89f0b25595c5dc3ce4af06c95c4c261075a1f0226d8af3f3dee'; const MANIFEST_SHA_AMENDMENT_6 = '0b55d8e353299594254e1a4a76f26f53014d726315dc6a0e5d6dc1a3a44a368a'; const MANIFEST_SHA_AMENDMENT_7 = 'bc0bcf9bd8b0c8344b25e5f8ab15b0475039ba28a1f782ebffe4cc1c4ff7d1de'; const MANIFEST_SHA_AMENDMENT_8 = '85858f12f1270da28277dd4d98e454d1dae8ef970537cb8c561f484599c4e2e9'; const MANIFEST_SHA_AMENDMENT_9 = '5e3ad831c61beb19ccb4ff42b455b4c3964d830808944d4915189c5e9b1709b8'; const MANIFEST_SHA_AMENDMENT_10 = '7fb2fb930670b5a28e417a76c64ca1a556f05afb9cf0761aba9f83f0c5de1c9b'; // ── Amendment 7 — mid-run halt thresholds (binding) ─────────────────────── // Per manifest v7 Amendment 7 §checkpoint_b_tightened.mid_run_halt_thresholds. /** Per-eval cost projection from Checkpoint A v2 §E (USD). */ const PER_EVAL_COST_PROJECTION_USD = 0.1243; /** Mid-run halt: per-candidate cost overshoot threshold (>25% over projection = >$0.156/eval). */ const PER_CANDIDATE_COST_OVERSHOOT_THRESHOLD_USD = PER_EVAL_COST_PROJECTION_USD * 1.25; // 0.155375 /** Mid-run halt: count of candidates with overshoot that triggers halt (>3). */ const MID_RUN_HALT_OVERSHOOT_CANDIDATE_COUNT = 3; /** Mid-run halt: per-shape variance widens (max-min trio_strict_pass_rate_II range across candidates) >40pp. */ const PER_SHAPE_VARIANCE_HALT_PP = 40; /** Minimum evals before per-shape variance check runs (avoid noise on N<3). */ const PER_SHAPE_VARIANCE_MIN_EVALS = 3; /** * Minimum Qwen evals before retrieval regression check runs. * * Amendment 10 §10.1 calibration_fix: raised from 3 → 5 based on empirical * evidence from 2 prior halt firings (b5avslp51 sunk + b1t474yqd full Gen 1) * where halt fired on baseline-only data within ±0.10 absolute noise band. * Each candidate must have 5+ evals to enter the per-shape aggregate check; * reduces N=3 binomial-tail noise sensitivity. */ const QWEN_RETRIEVAL_REGRESSION_MIN_EVALS = 5; // Amendment 10 §10.1 (was 3 per Amendment 7) function log(msg: string): void { const line = `[${new Date().toISOString()}] ${msg}\n`; try { fs.appendFileSync(RUN_LOG, line); } catch { /* dir may not exist */ } process.stderr.write(line); } interface Args { mode: 'dry-run' | 'checkpoint-b' | 'full'; } function parseArgs(argv: string[]): Args { let mode: Args['mode'] = 'dry-run'; for (const f of argv) { if (f === '--dry-run') mode = 'dry-run'; else if (f === '--checkpoint-b') mode = 'checkpoint-b'; else if (f === '--full') mode = 'full'; } return { mode }; } // ── Mulberry32 sampling (same as NULL-baseline) ─────────────────────────── function mulberry32(seed: number): () => number { let t = seed >>> 0; return () => { t = (t + 0x6d2b79f5) >>> 0; let r = t; r = Math.imul(r ^ (r >>> 15), r | 1); r ^= r + Math.imul(r ^ (r >>> 7), r | 61); return ((r ^ (r >>> 14)) >>> 0) / 4294967296; }; } function deterministicShuffle(items: ReadonlyArray, seed: number): T[] { const arr = [...items]; const rand = mulberry32(seed); for (let i = arr.length - 1; i > 0; i--) { const j = Math.floor(rand() * (i + 1)); [arr[i], arr[j]] = [arr[j], arr[i]]; } return arr; } function loadCorpus(): CorpusInstance[] { return fs.readFileSync(CORPUS_JSONL, 'utf-8').trim().split(/\n+/).filter(Boolean).map(l => JSON.parse(l)); } // ── Candidate loader (baseline from REGISTRY + 2 mutations dynamic import) ─ interface Candidate { candidateId: string; // e.g., qwen-thinking::baseline | qwen-thinking::gen1-v1 shape: ShapeName; variant: 'baseline' | 'gen1-v1' | 'gen1-v2'; promptShape: PromptShape; } async function loadCandidates(): Promise> { const out = new Map(); for (const shape of SHAPES) { const cands: Candidate[] = []; const baseline = REGISTRY[shape]; if (!baseline) throw new Error(`shape "${shape}" not in REGISTRY`); cands.push({ candidateId: `${shape}::baseline`, shape, variant: 'baseline', promptShape: baseline }); for (let v = 1; v <= 2; v++) { const filename = `${shape}-gen1-v${v}.ts`; const filepath = path.join(GEPA_EVOLVED_DIR, filename); if (!fs.existsSync(filepath)) { throw new Error(`mutation file missing: ${filepath}`); } // Windows ESM requires file:// URL for absolute paths const mod: any = await import(pathToFileURL(filepath).href); // Find the exported PromptShape (single export per file convention) const promptShape = Object.values(mod).find( (v: any) => v && typeof v === 'object' && 'name' in v && 'systemPrompt' in v && 'soloUserPrompt' in v, ) as PromptShape | undefined; if (!promptShape) throw new Error(`no PromptShape export found in ${filepath}`); cands.push({ candidateId: `${shape}::gen1-v${v}`, shape, variant: `gen1-v${v}` as 'gen1-v1' | 'gen1-v2', promptShape, }); } out.set(shape, cands); } return out; } // ── LLM call adapter (same as NULL-baseline) ────────────────────────────── const llmCall: LlmCallFn = async (input: LlmCallInput): Promise => { const masterKey = process.env.LITELLM_MASTER_KEY; if (!masterKey) throw new Error('LITELLM_MASTER_KEY env not set'); const { model, messages } = input; const isQwen = model.includes('qwen'); const maxTokens = input.maxTokens ?? (isQwen ? SUBJECT_MAX_TOKENS : 4096); const thinking = input.thinking ?? (isQwen ? SUBJECT_THINKING : true); const payload: Record = { model, messages, max_tokens: maxTokens }; if (model.startsWith('claude-opus')) payload.temperature = 1.0; else if (model === 'gpt-5.4' || model === 'minimax-m27-via-openrouter') {/* omit */} else payload.temperature = input.temperature ?? 0.3; if (isQwen) payload.extra_body = { enable_thinking: thinking }; const started = Date.now(); let lastErr: string | undefined; for (let attempt = 0; attempt < 2; attempt++) { try { const resp = await fetch(`${LITELLM_URL}/chat/completions`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${masterKey}` }, body: JSON.stringify(payload), }); const d: any = await resp.json(); if ('error' in d) { lastErr = String(d.error?.message ?? JSON.stringify(d.error)).slice(0, 200); if (attempt < 1) await new Promise(r => setTimeout(r, 1500)); continue; } const content = d.choices?.[0]?.message?.content ?? ''; const usage = d.usage ?? {}; const inTok = usage.prompt_tokens ?? 0; const outTok = usage.completion_tokens ?? 0; const pricing = MODEL_PRICING[model] ?? { in: 1, out: 4 }; return { content, inTokens: inTok, outTokens: outTok, costUsd: (inTok*pricing.in + outTok*pricing.out)/1_000_000, latencyMs: Date.now()-started }; } catch (e) { lastErr = `${(e as Error).name}: ${(e as Error).message}`.slice(0, 200); if (attempt < 1) await new Promise(r => setTimeout(r, 1500)); } } return { content: '', inTokens: 0, outTokens: 0, costUsd: 0, latencyMs: Date.now()-started, error: lastErr }; }; // ── Trio judging (mirror NULL-baseline) ─────────────────────────────────── const JUDGE_PROMPT_TEMPLATE = `You are evaluating an AI agent's response to a complex knowledge work task. The persona, scenario, materials, and question are provided. The response was generated under one of four configurations (revealed only after scoring): {model_only | model + memory + agent harness} × {Opus 4.7 | Qwen 3.6 35B-A3B}. You do NOT know which configuration produced this response. Score blind. Read the persona/scenario/question (provided), skim the materials (provided), then read the response carefully (provided). Score the response on six dimensions, Likert 1-5: 1. COMPLETENESS — engagement with all material 2. ACCURACY — faithfulness to source materials, no hallucinations 3. SYNTHESIS — connections across inputs, not isolated treatment 4. JUDGMENT — defensible recommendations, tradeoffs acknowledged 5. ACTIONABILITY — would the persona act on this tomorrow 6. STRUCTURE — organization and readability Output JSON only: {"completeness":<1-5>,"accuracy":<1-5>,"synthesis":<1-5>,"judgment":<1-5>,"actionability":<1-5>,"structure":<1-5>,"rationale":"<1-2 sentences explaining the lowest scoring dimension>","overall_verdict":""} PASS_STRONG: mean >= 4.0 PASS_ADEQUATE: mean 3.5-3.99 FAIL_WEAK: mean 2.5-3.49 FAIL_CRITICAL: mean < 2.5 [PERSONA + SCENARIO + QUESTION] ###PSQ### [MATERIALS] ###MAT### [RESPONSE TO EVALUATE] ###RES###`; interface JudgeRecord { judge_model: string; mean: number; cost: number; latency_ms: number; raw: any; retries: number } interface TrioResult { records: JudgeRecord[]; trioMean: number; trioStrictPassII: boolean; trioStrictPassI: boolean; cost: number } function parseJudgeJson(text: string): { mean: number; raw: any } | null { const m = text.match(/\{[\s\S]*\}/); if (!m) return null; try { const obj = JSON.parse(m[0]); const dims = ['completeness','accuracy','synthesis','judgment','actionability','structure']; for (const d of dims) if (typeof obj[d] !== 'number' || obj[d] < 1 || obj[d] > 5) return null; const mean = dims.reduce((s, d) => s + obj[d], 0) / dims.length; return { mean, raw: obj }; } catch { return null; } } async function runJudge(model: string, prompt: string): Promise { let totalCost = 0, totalLat = 0; for (let attempt = 0; attempt < JUDGE_RETRIES; attempt++) { const r = await llmCall({ model, messages: [{ role: 'user', content: prompt }], maxTokens: JUDGE_MAX_TOKENS, thinking: false }); totalCost += r.costUsd; totalLat += r.latencyMs; if (r.error) continue; const parsed = parseJudgeJson(r.content); if (parsed) return { judge_model: model, mean: parsed.mean, cost: totalCost, latency_ms: totalLat, raw: parsed.raw, retries: attempt }; } return { judge_model: model, mean: 0, cost: totalCost, latency_ms: totalLat, raw: null, retries: JUDGE_RETRIES }; } async function judgeTrio(instance: CorpusInstance, response: string): Promise { const prompt = JUDGE_PROMPT_TEMPLATE .replace('###PSQ###', `${instance.personaText}\n\nQUESTION: ${instance.question}`) .replace('###MAT###', instance.materialsConcat) .replace('###RES###', response); const records = await Promise.all(JUDGES.map(j => runJudge(j, prompt))); const valid = records.filter(r => r.mean > 0).map(r => r.mean); const trioMean = valid.length > 0 ? valid.reduce((a,b)=>a+b,0)/valid.length : 0; return { records, trioMean, trioStrictPassII: trioMean >= 4.0, trioStrictPassI: records.filter(r => r.mean >= 3.5).length >= 2, cost: records.reduce((s,r) => s + r.cost, 0), }; } // ── Per-eval orchestration ──────────────────────────────────────────────── interface EvalRecord { shape: ShapeName; candidateId: string; variant: Candidate['variant']; instanceId: string; instanceCell: CorpusInstance['cell']; candidateResponse: string; candidateLatencyMs: number; candidateCostUsd: number; loopExhausted: boolean; stepsTaken: number; retrievalCalls: number; trioMean: number; trioStrictPassII: boolean; trioStrictPassI: boolean; judgeRecords: JudgeRecord[]; evalCostUsd: number; manifestAnchor: string; manifestShaAmendment5: string; tsIso: string; } async function runOneEval(cand: Candidate, instance: CorpusInstance, embedder: Embedder): Promise { const evalId = `${cand.candidateId}__${instance.instanceId}`; log(`[${evalId}] start`); const dbPath = path.join(SCRATCH_DIR, `eval-${cand.candidateId.replace(/[:]/g, '_')}-${instance.instanceId}.sqlite`); if (fs.existsSync(dbPath)) fs.unlinkSync(dbPath); const db = new MindDB(dbPath); const frames = new FrameStore(db); const sessions = new SessionStore(db); const hybrid = new HybridSearch(db, embedder); const gopId = `gen1-${cand.candidateId.replace(/[:]/g,'_')}-${instance.instanceId}`; sessions.ensure(gopId, undefined, `Gen 1 ${cand.candidateId} on ${instance.instanceId}`); for (const doc of instance.sourceDocuments) frames.createIFrame(gopId, `## ${doc.title}\n\n${doc.body}`, 'important', 'system'); const search: RetrievalSearchFn = async ({ query, limit }) => { const hits = await hybrid.search(query, { limit, gopId }); return { formattedResults: hits.length > 0 ? hits.map((s,i)=>`[result ${i+1}, score ${s.finalScore.toFixed(3)}]\n${s.frame.content}`).join('\n\n---\n\n') : '', resultCount: hits.length, }; }; // Inject the candidate's prompt shape via custom orchestration: we use runRetrievalAgentLoop // with the candidate's modelAlias + prompt-shape. The agent loop internally selects shape via // selector; we override by passing the candidate's shape directly. Since runRetrievalAgentLoop // uses selectShape internally, we override REGISTRY at runtime by name match. // For Faza 1 simplicity, we register candidate as override under its unique name: let agentResult: AgentRunResult; try { // Amendment 8 §canonical_mutation_api: register the candidate via the sanctioned // mutation path. registerShape() is imported from '@waggle/agent' so it mutates // the SAME REGISTRY instance the agent-loop's selectShape() reads from. Direct // (REGISTRY as any)[name] = shape is forbidden post-Amendment-8 (would mutate a // separate module instance under tsx + Node ESM workspace resolution). registerShape(cand.promptShape.name, cand.promptShape); agentResult = await runRetrievalAgentLoop({ modelAlias: SUBJECT_ALIAS, persona: instance.personaText, question: instance.question, llmCall, search, maxSteps: MAX_STEPS, maxRetrievalsPerStep: MAX_RETRIEVALS_PER_STEP, perCallHaltUsd: PER_CALL_HALT_USD, perCellHaltUsd: PER_CELL_HALT_USD, contextTag: evalId, promptShapeOverride: cand.promptShape.name, // if supported } as any); } catch (e) { return { error: `agent loop failed: ${(e as Error).message}` }; } log(`[${evalId}] subject_done; retrievals=${agentResult.retrievalCalls} steps=${agentResult.stepsTaken} cost=$${agentResult.totalCostUsd.toFixed(4)}`); const judges = await judgeTrio(instance, agentResult.rawResponse); const evalCostUsd = agentResult.totalCostUsd + judges.cost; log(`[${evalId}] judged; trio_mean=${judges.trioMean.toFixed(3)} pass_ii=${judges.trioStrictPassII} retrievals=${agentResult.retrievalCalls} eval_cost=$${evalCostUsd.toFixed(4)}`); return { shape: cand.shape, candidateId: cand.candidateId, variant: cand.variant, instanceId: instance.instanceId, instanceCell: instance.cell, candidateResponse: agentResult.rawResponse, candidateLatencyMs: agentResult.totalLatencyMs, candidateCostUsd: agentResult.totalCostUsd, loopExhausted: agentResult.loopExhausted, stepsTaken: agentResult.stepsTaken, retrievalCalls: agentResult.retrievalCalls, trioMean: judges.trioMean, trioStrictPassII: judges.trioStrictPassII, trioStrictPassI: judges.trioStrictPassI, judgeRecords: judges.records, evalCostUsd, manifestAnchor: MANIFEST_ANCHOR, manifestShaAmendment5: MANIFEST_SHA_AMENDMENT_5, tsIso: new Date().toISOString(), }; } // ── Main ────────────────────────────────────────────────────────────────── // ── Amendment 7 — per-candidate accumulator ─────────────────────────────── interface CandidateAcc { candidateId: string; shape: ShapeName; variant: 'baseline' | 'gen1-v1' | 'gen1-v2'; evalCount: number; passIICount: number; // count of trioStrictPassII = true totalCostUsd: number; totalRetrievalCalls: number; trioMeans: number[]; // per-eval trioMean for variance + audit retrievalCalls: number[]; // per-eval retrieval calls for audit mutationValidatorPassed: boolean; // computed from validateCandidate at startup } function makeCandidateAcc(cand: Candidate, validatorPassed: boolean): CandidateAcc { return { candidateId: cand.candidateId, shape: cand.shape, variant: cand.variant, evalCount: 0, passIICount: 0, totalCostUsd: 0, totalRetrievalCalls: 0, trioMeans: [], retrievalCalls: [], mutationValidatorPassed: validatorPassed, }; } function ingestEvalIntoAcc(acc: CandidateAcc, r: EvalRecord): void { acc.evalCount++; if (r.trioStrictPassII) acc.passIICount++; acc.totalCostUsd += r.evalCostUsd; acc.totalRetrievalCalls += r.retrievalCalls; acc.trioMeans.push(r.trioMean); acc.retrievalCalls.push(r.retrievalCalls); } function accMeanCostPerEval(acc: CandidateAcc): number { return acc.evalCount > 0 ? acc.totalCostUsd / acc.evalCount : 0; } function accPassRateII(acc: CandidateAcc): number { return acc.evalCount > 0 ? acc.passIICount / acc.evalCount : 0; } function accMeanRetrievalCallsPerTask(acc: CandidateAcc): number { return acc.evalCount > 0 ? acc.totalRetrievalCalls / acc.evalCount : 0; } // ── Amendment 7 — mid-run halt check (binding) ──────────────────────────── interface MidRunHaltCheckResult { shouldHalt: boolean; reason: string | null; } function checkMidRunHalts(accs: Map): MidRunHaltCheckResult { // Threshold A — per-candidate cost overshoot >25% on >3 candidates let overshootCount = 0; const overshootCandidates: string[] = []; for (const acc of accs.values()) { if (acc.evalCount === 0) continue; if (accMeanCostPerEval(acc) > PER_CANDIDATE_COST_OVERSHOOT_THRESHOLD_USD) { overshootCount++; overshootCandidates.push(`${acc.candidateId}=$${accMeanCostPerEval(acc).toFixed(4)}/eval`); } } if (overshootCount > MID_RUN_HALT_OVERSHOOT_CANDIDATE_COUNT) { return { shouldHalt: true, reason: `Amendment 7 §checkpoint_b_tightened.per_candidate_cost_overshoot: ${overshootCount} candidates >$${PER_CANDIDATE_COST_OVERSHOOT_THRESHOLD_USD.toFixed(4)}/eval (threshold >${MID_RUN_HALT_OVERSHOOT_CANDIDATE_COUNT}); offenders=[${overshootCandidates.join(', ')}]`, }; } // Threshold B — per-shape variance widens >40pp range (max-min trio_strict_pass_rate_II) on any shape for (const shape of SHAPES) { const shapeAccs = [...accs.values()].filter(a => a.shape === shape && a.evalCount >= PER_SHAPE_VARIANCE_MIN_EVALS); if (shapeAccs.length < 2) continue; const passRates = shapeAccs.map(accPassRateII); const max = Math.max(...passRates); const min = Math.min(...passRates); const rangePP = (max - min) * 100; if (rangePP > PER_SHAPE_VARIANCE_HALT_PP) { return { shouldHalt: true, reason: `Amendment 7 §checkpoint_b_tightened.per_shape_variance_widens: shape=${shape} range=${rangePP.toFixed(1)}pp > ${PER_SHAPE_VARIANCE_HALT_PP}pp; rates=${passRates.map(r => r.toFixed(2)).join(',')}`, }; } } // Threshold C — Qwen-targeted retrieval engagement drops below per-shape NULL baseline // // Amendment 10 §10.1 mutation_execution_gate (binding): halt only fires when at // least one mutation candidate has been evaluated for this shape. Baseline-only // data does NOT trigger halt. This matches Amendment 9 §qwen_evolution_verdict_capture // .mid_run_halt_binding intent that the halt represents direction_2 verdict // (mutations regress retrieval), not baseline-running stochastic variance. // Empirical basis: 2 prior halt firings (b5avslp51 + b1t474yqd) on baseline-only // data within ±0.10 absolute noise band were both per-Amendment-9 NOT direction_2 // verdicts. for (const shape of ['qwen-thinking', 'qwen-non-thinking'] as const) { const allShapeAccs = [...accs.values()].filter(a => a.shape === shape); // Amendment 11 §11.1 second_order_calibration_patch (binding): // mutation_execution_gate threshold tightened from ≥1 eval to ≥MIN_EVALS evals. // Halt only fires when at least one mutation candidate (variant !== 'baseline') // has STATISTICALLY MEANINGFUL sample size (≥QWEN_RETRIEVAL_REGRESSION_MIN_EVALS=5 // evals). This guarantees the mutation IS in the per-shape aggregate (not // excluded by the individual-candidate MIN_EVALS filter), eliminating the // second-order false-positive class where halt fired on baseline-only // aggregate while gate was mechanically met by a single mutation eval. // // Per Amendment 11 §11.2 terminal_calibration_clause (BINDING): if halt // fires AGAIN with this calibration ACTIVE, that IS Phase 4.5 direction_2 // verdict. No further calibration patches; escalate to Option C (Amendment 12 // interface refactor). const hasStatisticallyMeaningfulMutationForShape = allShapeAccs.some( a => a.variant !== 'baseline' && a.evalCount >= QWEN_RETRIEVAL_REGRESSION_MIN_EVALS, ); if (!hasStatisticallyMeaningfulMutationForShape) continue; const baseline = NULL_BASELINE_PER_SHAPE[shape].meanRetrievalCallsPerTask; const shapeAccs = allShapeAccs.filter(a => a.evalCount >= QWEN_RETRIEVAL_REGRESSION_MIN_EVALS); if (shapeAccs.length === 0) continue; const totalRetr = shapeAccs.reduce((s, a) => s + a.totalRetrievalCalls, 0); const totalEvals = shapeAccs.reduce((s, a) => s + a.evalCount, 0); if (totalEvals === 0) continue; const aggMean = totalRetr / totalEvals; if (aggMean < baseline) { return { shouldHalt: true, reason: `Amendment 7 §checkpoint_b_tightened.qwen_retrieval_engagement_regression (post Amendment 11 §11.1 second_order_calibration_patch): shape=${shape} mean=${aggMean.toFixed(3)} < NULL baseline ${baseline.toFixed(3)} (n=${totalEvals}; ≥1 mutation candidate with ≥${QWEN_RETRIEVAL_REGRESSION_MIN_EVALS} evals evaluated for shape; per Amendment 11 §11.2 terminal_calibration_clause, this IS Phase 4.5 direction_2 verdict)`, }; } } return { shouldHalt: false, reason: null }; } // ── Amendment 7 — Checkpoint B summary writer (binding extensions) ──────── interface PerCandidateTierBreakdown { candidateId: string; shape: ShapeName; variant: string; evalCount: number; trioStrictPassRateII: number; meanRetrievalCallsPerTask: number; meanEvalCostUsd: number; costOvershoot: boolean; tieredFitness: TieredFitnessComponents; } interface CheckpointBSummary { manifestAnchor: string; manifestShaAmendment7: string; generated_at: string; mode: string; totalEvals: number; totalCostUsd: number; haltReason: string | null; perCandidateTierBreakdown: PerCandidateTierBreakdown[]; retrievalEngagementDeltasPerQwenShape: { 'qwen-thinking': { nullBaselineMean: number; gen1PartialMean: number | null; deltaAbsolute: number | null }; 'qwen-non-thinking': { nullBaselineMean: number; gen1PartialMean: number | null; deltaAbsolute: number | null }; }; cellSemanticAnchorInvarianceCountPerCandidate: Record; preRegisteredDeltaFloorVerdict: DeltaFloorVerdict; midRunHaltsBindingThresholds: { perCandidateCostOvershoot: { threshold: number; candidatesOvershoot: number }; perShapeVariance: { thresholdPP: number; maxRangeObservedPP: number }; qwenRetrievalRegression: { triggered: boolean; details: string }; }; } function buildCheckpointBSummary( args: ReturnType, accs: Map, totalEvals: number, totalCostUsd: number, haltReason: string | null, ): CheckpointBSummary { const perCandidate: PerCandidateTierBreakdown[] = []; for (const acc of accs.values()) { if (acc.evalCount === 0) continue; const passRate = accPassRateII(acc); const meanRetr = accMeanRetrievalCallsPerTask(acc); const meanCost = accMeanCostPerEval(acc); const candidateMetrics = { candidateId: acc.candidateId, shape: acc.shape, evaluations: [], trioStrictPassRateII: passRate, trioStrictPassRateI: 0, // not tracked here; reported in JSONL meanRetrievalCallsPerTask: meanRetr, meanCostUsd: meanCost, }; const tieredFitness = computeTieredFitness({ candidate: candidateMetrics, nullBaselinePassRateII: NULL_BASELINE_PER_SHAPE[acc.shape].trioStrictPassRateII, nullBaselineMeanRetrievalCallsPerTask: NULL_BASELINE_PER_SHAPE[acc.shape].meanRetrievalCallsPerTask, mutationValidatorPassed: acc.mutationValidatorPassed, saturatedRegime: true, // 5/5 shapes ≥75% per Checkpoint A v2 §B.2 }); perCandidate.push({ candidateId: acc.candidateId, shape: acc.shape, variant: acc.variant, evalCount: acc.evalCount, trioStrictPassRateII: passRate, meanRetrievalCallsPerTask: meanRetr, meanEvalCostUsd: meanCost, costOvershoot: meanCost > PER_CANDIDATE_COST_OVERSHOOT_THRESHOLD_USD, tieredFitness, }); } // Aggregate Tier 1: mean trio_strict_pass_rate_II across all evals const totalEvalsAcc = perCandidate.reduce((s, c) => s + c.evalCount, 0); const aggregateTrioStrictPassRateII = totalEvalsAcc > 0 ? perCandidate.reduce((s, c) => s + c.trioStrictPassRateII * c.evalCount, 0) / totalEvalsAcc : 0; // Per-shape Qwen retrieval means (across that shape's candidates) function qwenShapeAggregate(shape: 'qwen-thinking' | 'qwen-non-thinking'): { gen1PartialMean: number | null; deltaAbsolute: number | null } { const shapeAccs = [...accs.values()].filter(a => a.shape === shape && a.evalCount > 0); if (shapeAccs.length === 0) return { gen1PartialMean: null, deltaAbsolute: null }; const totalRetr = shapeAccs.reduce((s, a) => s + a.totalRetrievalCalls, 0); const totalEvalsLocal = shapeAccs.reduce((s, a) => s + a.evalCount, 0); const mean = totalEvalsLocal > 0 ? totalRetr / totalEvalsLocal : null; const baseline = NULL_BASELINE_PER_SHAPE[shape].meanRetrievalCallsPerTask; return { gen1PartialMean: mean, deltaAbsolute: mean === null ? null : mean - baseline }; } const qwenThinkingAgg = qwenShapeAggregate('qwen-thinking'); const qwenNonThinkingAgg = qwenShapeAggregate('qwen-non-thinking'); const qwenShapeRetrievalMeans: Partial> = {}; if (qwenThinkingAgg.gen1PartialMean !== null) qwenShapeRetrievalMeans['qwen-thinking'] = qwenThinkingAgg.gen1PartialMean; if (qwenNonThinkingAgg.gen1PartialMean !== null) qwenShapeRetrievalMeans['qwen-non-thinking'] = qwenNonThinkingAgg.gen1PartialMean; // Aggregate Tier 2 bonus across Qwen-targeted candidates (mean across qwen candidates with data) const qwenCandidates = perCandidate.filter(c => c.shape === 'qwen-thinking' || c.shape === 'qwen-non-thinking'); const qwenAggregateTier2Bonus = qwenCandidates.length > 0 ? qwenCandidates.reduce((s, c) => s + c.tieredFitness.tier2RetrievalBonus, 0) / qwenCandidates.length : 0; const deltaFloorVerdict = computeDeltaFloorVerdict({ aggregateTrioStrictPassRateII, aggregateNullBaselinePassRateII: NULL_BASELINE_AGGREGATE.trioStrictPassRateII, qwenShapeRetrievalMeans, qwenShapeNullBaselineRetrievalMeans: { 'qwen-thinking': NULL_BASELINE_PER_SHAPE['qwen-thinking'].meanRetrievalCallsPerTask, 'qwen-non-thinking': NULL_BASELINE_PER_SHAPE['qwen-non-thinking'].meanRetrievalCallsPerTask, }, qwenAggregateTier2Bonus, }); // Per-shape variance maxRange snapshot let maxRangeObservedPP = 0; for (const shape of SHAPES) { const shapeAccs = perCandidate.filter(c => c.shape === shape); if (shapeAccs.length < 2) continue; const rates = shapeAccs.map(c => c.trioStrictPassRateII); const range = (Math.max(...rates) - Math.min(...rates)) * 100; if (range > maxRangeObservedPP) maxRangeObservedPP = range; } const overshootCount = perCandidate.filter(c => c.costOvershoot).length; // Qwen retrieval regression check (binary informational; halt logic in checkMidRunHalts) let qwenRegressionDetails = 'no_regression'; let qwenRegressionTriggered = false; for (const shape of ['qwen-thinking', 'qwen-non-thinking'] as const) { const agg = shape === 'qwen-thinking' ? qwenThinkingAgg : qwenNonThinkingAgg; if (agg.gen1PartialMean !== null && agg.gen1PartialMean < NULL_BASELINE_PER_SHAPE[shape].meanRetrievalCallsPerTask) { qwenRegressionTriggered = true; qwenRegressionDetails = `${shape} mean=${agg.gen1PartialMean.toFixed(3)} < NULL ${NULL_BASELINE_PER_SHAPE[shape].meanRetrievalCallsPerTask}`; break; } } const cellSemanticAnchorInvarianceCountPerCandidate: Record = {}; for (const c of perCandidate) { cellSemanticAnchorInvarianceCountPerCandidate[c.candidateId] = c.tieredFitness.cellSemanticAnchorInvarianceCount; } return { manifestAnchor: MANIFEST_ANCHOR, manifestShaAmendment7: MANIFEST_SHA_AMENDMENT_7, generated_at: new Date().toISOString(), mode: args.mode, totalEvals, totalCostUsd, haltReason, perCandidateTierBreakdown: perCandidate, retrievalEngagementDeltasPerQwenShape: { 'qwen-thinking': { nullBaselineMean: NULL_BASELINE_PER_SHAPE['qwen-thinking'].meanRetrievalCallsPerTask, gen1PartialMean: qwenThinkingAgg.gen1PartialMean, deltaAbsolute: qwenThinkingAgg.deltaAbsolute, }, 'qwen-non-thinking': { nullBaselineMean: NULL_BASELINE_PER_SHAPE['qwen-non-thinking'].meanRetrievalCallsPerTask, gen1PartialMean: qwenNonThinkingAgg.gen1PartialMean, deltaAbsolute: qwenNonThinkingAgg.deltaAbsolute, }, }, cellSemanticAnchorInvarianceCountPerCandidate, preRegisteredDeltaFloorVerdict: deltaFloorVerdict, midRunHaltsBindingThresholds: { perCandidateCostOvershoot: { threshold: PER_CANDIDATE_COST_OVERSHOOT_THRESHOLD_USD, candidatesOvershoot: overshootCount, }, perShapeVariance: { thresholdPP: PER_SHAPE_VARIANCE_HALT_PP, maxRangeObservedPP, }, qwenRetrievalRegression: { triggered: qwenRegressionTriggered, details: qwenRegressionDetails, }, }, }; } // ── Main ────────────────────────────────────────────────────────────────── async function main(): Promise { const args = parseArgs(process.argv.slice(2)); fs.mkdirSync(OUT_DIR, { recursive: true }); fs.mkdirSync(SCRATCH_DIR, { recursive: true }); if (!fs.existsSync(RUN_LOG)) fs.writeFileSync(RUN_LOG, ''); const corpus = loadCorpus(); const sample = deterministicShuffle(corpus, SAMPLING_SEED).slice(0, N_PER_SHAPE); log(`[loaded] corpus=${corpus.length}; sample=${N_PER_SHAPE} via seed=${SAMPLING_SEED}`); const candidates = await loadCandidates(); log(`[loaded] ${SHAPES.length} shapes × ${N_CANDIDATES_PER_SHAPE} candidates each`); // Amendment 7 — pre-validate all candidates against cell-semantic anchors (Tier 3 input) const candidateValidatorVerdicts = new Map(); const TYPES_FILE_PATH = path.join(PROMPT_SHAPES_DIR, 'types.ts'); for (const shape of SHAPES) { for (const cand of candidates.get(shape)!) { if (cand.variant === 'baseline') { // Baselines pass by definition (they ARE the pinned shape file) candidateValidatorVerdicts.set(cand.candidateId, null); // null = baseline (Tier 3 = 0.10 by anchor invariance) continue; } const filename = `${shape}-${cand.variant}.ts`; const candPath = path.join(GEPA_EVOLVED_DIR, filename); try { const verdict = validateCandidate({ candidateShapeFilePath: candPath, baselineShapeName: `${shape}.ts`, typesFilePath: TYPES_FILE_PATH, expectShapeDiff: true, }); candidateValidatorVerdicts.set(cand.candidateId, verdict); log(`[validator] ${cand.candidateId} valid=${verdict.valid} violations=${verdict.violations.length}`); } catch (e) { log(`[validator] ${cand.candidateId} ERROR ${(e as Error).message}`); candidateValidatorVerdicts.set(cand.candidateId, null); } } } if (args.mode === 'dry-run') { log(`[dry-run] would run 5×3×8 = 120 evals (or halt at 30 = Checkpoint B)`); for (const shape of SHAPES) { for (const cand of candidates.get(shape)!) { log(`[dry-run] candidate=${cand.candidateId} variant=${cand.variant} shape.name=${cand.promptShape.name}`); } } return; } // Resume support const existing = new Set(); let cumulativeCost = 0; // Amendment 7 — per-candidate accumulator (rebuilt from JSONL on resume) const accs = new Map(); for (const shape of SHAPES) { for (const cand of candidates.get(shape)!) { const verdict = candidateValidatorVerdicts.get(cand.candidateId); // Baselines: validatorPassed = true (anchor invariant by definition). // Mutations: validatorPassed = verdict.valid (or false if validator threw). const validatorPassed = cand.variant === 'baseline' ? true : verdict?.valid ?? false; accs.set(cand.candidateId, makeCandidateAcc(cand, validatorPassed)); } } if (fs.existsSync(OUT_JSONL)) { for (const line of fs.readFileSync(OUT_JSONL, 'utf-8').trim().split(/\n+/).filter(Boolean)) { try { const r = JSON.parse(line) as EvalRecord; existing.add(`${r.candidateId}__${r.instanceId}`); cumulativeCost += r.evalCostUsd; const acc = accs.get(r.candidateId); if (acc) ingestEvalIntoAcc(acc, r); } catch { /* skip */ } } log(`[resume] loaded ${existing.size} existing evals; cumulative $${cumulativeCost.toFixed(4)}`); } const out = fs.createWriteStream(OUT_JSONL, { flags: existing.size > 0 ? 'a' : 'w' }); const embedder = createOllamaEmbedder({ baseUrl: OLLAMA_URL, model: EMBEDDER_MODEL }); const haltAt = args.mode === 'checkpoint-b' ? CHECKPOINT_B_HALT_EVALS : 120; log(`[mode=${args.mode}] target eval count: ${haltAt}`); let nDone = existing.size; let amendment7HaltReason: string | null = null; outer: for (const shape of SHAPES) { for (const cand of candidates.get(shape)!) { for (const inst of sample) { const key = `${cand.candidateId}__${inst.instanceId}`; if (existing.has(key)) { log(`[skip] ${key} already in JSONL`); continue; } if (nDone >= haltAt) { log(`[HALT] reached ${haltAt} evals (Checkpoint B)`); break outer; } if (cumulativeCost >= COST_HALT_USD) { log(`[HALT] cumulative $${cumulativeCost.toFixed(4)} >= $${COST_HALT_USD}`); break outer; } const r = await runOneEval(cand, inst, embedder); if ('error' in r) { log(`[skip] ${key}: ${r.error}`); continue; } out.write(JSON.stringify(r) + '\n'); cumulativeCost += r.evalCostUsd; nDone++; // Amendment 7 — update accumulator + check mid-run halts const acc = accs.get(cand.candidateId); if (acc) ingestEvalIntoAcc(acc, r); const haltCheck = checkMidRunHalts(accs); if (haltCheck.shouldHalt) { amendment7HaltReason = haltCheck.reason; log(`[HALT-A7] ${haltCheck.reason}`); break outer; } log(`[cumulative] $${cumulativeCost.toFixed(4)} / $${COST_HALT_USD} halt; ${nDone} evals total`); } } } out.end(); // Amendment 7 — write Checkpoint B summary (binding extension per §checkpoint_b_tightened.report_extensions) const summary = buildCheckpointBSummary(args, accs, nDone, cumulativeCost, amendment7HaltReason); fs.writeFileSync(SUMMARY_JSON, JSON.stringify(summary, null, 2)); log(`[summary] wrote ${SUMMARY_JSON}`); log(`[delta-floor] verdict=${summary.preRegisteredDeltaFloorVerdict.overallVerdict}`); log(`[delta-floor] threshold_1_aggregate_tier_1=${summary.preRegisteredDeltaFloorVerdict.threshold1AggregateTier1} (value=${summary.preRegisteredDeltaFloorVerdict.threshold1ValuePP.toFixed(2)}pp)`); log(`[delta-floor] threshold_2_qwen_retrieval_absolute=${summary.preRegisteredDeltaFloorVerdict.threshold2QwenRetrievalAbsolute} (max_delta=${summary.preRegisteredDeltaFloorVerdict.threshold2MaxDeltaAbsolute.toFixed(3)})`); log(`[delta-floor] threshold_3_compound_tier_1_plus_tier_2=${summary.preRegisteredDeltaFloorVerdict.threshold3CompoundTier1PlusTier2} (tier1=${summary.preRegisteredDeltaFloorVerdict.threshold3Tier1ValuePP.toFixed(2)}pp tier2_agg=${summary.preRegisteredDeltaFloorVerdict.threshold3Tier2Aggregate.toFixed(3)})`); log(`[done] ${nDone} evals; total cost $${cumulativeCost.toFixed(4)}; halt_reason=${amendment7HaltReason ?? 'none (Checkpoint B reached or completed)'}`); } main().catch(e => { console.error('FATAL:', e); process.exit(2); });