moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
# Faza 1 closed — quarantined tests
**Quarantine date:** 2026-04-29 (Phase 5 §0 preflight)
**Authority:** PM ratification — Phase 5 deployment §0 preflight 3-ask response, Ask #1 Option 1
**Branch:** `phase-5-deployment-v2`
**Quarantine commit:** see git log this directory
**Vitest exclude:** `**/__faza1-closed/**` added to `vitest.config.ts` exclude list — these files are skipped from collection (no longer load-bearing for current verification).
---
## Quarantined files
| File | Reason |
|---|---|
| `mutation-validator.test.ts` | Pins baseline shape SHAs at Faza 1 substrate freeze `c9bda3d`; `phase-5-deployment-v2` Opcija C inheritance chain doesn't reach `c9bda3d`. PM Ask #1 Option 1 ratified 2026-04-29. |
| `registry-injection.test.ts` | Asserts Amendment 8 H1 failure mode (deep-relative-path REGISTRY ≠ package REGISTRY) reproduces under Node 22.19.0; current Node ESM resolver deduplicates the paths so the assertion fails. CC extended quarantine 2026-04-29 same session — flagged in §0 preflight evidence + commit message; PM advised. |
## Why these tests live here
Tests in this directory pin substrate SHAs against the Faza 1 substrate freeze head `c9bda3d` (Phase 4.7 HEAD on `feature/c3-v3-wrapper`, per `benchmarks/preregistration/manifest-v7-gepa-faza1.yaml` `substrate_freeze_head`). They were load-bearing during Faza 1 GEPA evolution runs (executed inside isolated worktree `D:/Projects/waggle-os-faza1-wt` rooted at `c9bda3d`) to enforce the cell-semantic boundary invariant — the mutation oracle must not modify baseline shape file content during Gen 1+ candidate generation.
`registry-injection.test.ts` documents a SECOND class of Faza 1 closed-work artifact: tests that intentionally assert a buggy state reproduces (so the bug stays detectable if someone "fixes" the canonical path back). Once Amendment 8 fixed H1 via the canonical `registerShape` API + Node ESM resolver dedup behavior changed in subsequent versions, these documentation tests can no longer pass — but their failure carries no Phase 5 substrate signal. The H1 fix is verified independently via `selectShape` + `registerShape` integration tests in the agent suite.
After Faza 1 closure (`6bc2089` — Checkpoint C closure decision `decisions/2026-04-29-gepa-faza1-results.md`), the branch architecture decision **Opcija C** (`decisions/2026-04-30-branch-architecture-opcija-c.md`) determined that:
- `phase-5-deployment-v2` inherits `gepa-faza-1` baseline (= `6bc2089`)
- `gepa-faza-1` parent chain reaches `origin/main` (`5ec069e`), NOT `c9bda3d`
- `c9bda3d` is on the divergent `feature/c3-v3-wrapper` branch (CC-1 Phase 4 work)
- Therefore the SHAs of baseline shape files on `phase-5-deployment-v2` reflect `origin/main` content, not the `c9bda3d` content these tests pin
Running these tests on `phase-5-deployment-v2` produces 14 failures with no Phase 5 substrate signal — the failures are a scope-leakage artifact of post-closure test continuation under Opcija C inheritance, not a bug in either Phase 5 substrate or Faza 1 evolution invariants.
Faza 1 closure verdict §F.4 already documents `105/105 anchor invariance checks PASS` during in-worktree execution + `15/15 held-out anchor checks PASS` during Checkpoint C — the cell-semantic boundary discipline was verified and binding throughout Faza 1.
## What this quarantine does and does NOT mean
- **Does NOT mean** Faza 1 substrate discipline was wrong or the test was buggy.
- **Does NOT mean** baseline shape files have been modified.
- **Does mean** the SHAs the test pins to (`c9bda3d` substrate snapshot) are not reachable from `phase-5-deployment-v2` HEAD without integration sprint work.
- **Does mean** the test is no longer load-bearing for Phase 5 deployment substrate verification (REGISTRY API + registerShape canonical path + gen1-v1 shape definitions are verified independently via Phase 5 §0.1 substrate readiness grep).
## Reactivation conditions
These tests should be re-activated (moved back out of `__faza1-closed/`) when ANY of the following holds:
1. **Post-Phase-5 production-stable integration sprint** (per Opcija C §5) merges `feature/c3-v3-wrapper` into the deployment lineage. Re-pin the test SHAs to the integrated substrate snapshot before re-activating.
2. **Future Faza N evolution sprints** that re-establish substrate freeze inside an isolated worktree. Re-activate the tests inside that worktree's branch context, not on the deployment branch.
3. **Substrate boundary regression suspected** — re-pin SHAs to the current deployment branch HEAD content and re-activate as a drift detector for that specific branch.
Forbidden: simply blanking the SHA pins to silence the test. Replacement pins must be anchored to a documented substrate snapshot with audit-traceable origin.
## Audit trail
| Anchor | Path |
|---|---|
| Faza 1 closure decision | `D:/Projects/PM-Waggle-OS/decisions/2026-04-29-gepa-faza1-results.md` |
| Branch architecture (Opcija C) | `D:/Projects/PM-Waggle-OS/decisions/2026-04-30-branch-architecture-opcija-c.md` |
| Phase 5 brief LOCKED | `D:/Projects/PM-Waggle-OS/briefs/2026-04-29-phase-5-deployment-brief-v1.md` |
| §0 preflight evidence | `D:/Projects/waggle-os/gepa-phase-5/preflight-evidence.md` |
| Quarantine ratification | PM 3-ask response 2026-04-29, Ask #1 Option 1 |

View File

@@ -0,0 +1,254 @@
/**
* GEPA Faza 1 — mutation validator tests.
*
* Coverage targets:
* - SHA boundary checks (types.ts + MULTI_STEP_ACTION_CONTRACT bytes)
* - Pinned baseline shape SHAs (5 shapes)
* - Locked metadata field detection
* - Imports preservation check
* - Gen 1 mutation must differ from baseline
* - Gen 0 baseline (expectShapeDiff=false) accepts identity
*
* Validation against actual substrate-pinned files (the worktree at c9bda3d).
*/
import { describe, expect, it } from 'vitest';
import * as path from 'node:path';
import * as fs from 'node:fs';
import * as os from 'node:os';
import {
BOUNDARY_SHAS,
BASELINE_SHAPE_SHAS,
sha256,
sha256File,
extractMultiStepActionContractBytes,
validateCandidate,
} from '../../src/faza-1/mutation-validator.js';
const WORKTREE_ROOT = path.resolve(__dirname, '../../../../');
const PROMPT_SHAPES_DIR = path.join(WORKTREE_ROOT, 'packages/agent/src/prompt-shapes');
const TYPES_FILE = path.join(PROMPT_SHAPES_DIR, 'types.ts');
// ───────────────────────────────────────────────────────────────────────────
// SHA primitive tests
// ───────────────────────────────────────────────────────────────────────────
describe('sha256 primitive', () => {
it('computes deterministic SHA-256 of utf-8 string', () => {
expect(sha256('hello')).toBe(
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
);
});
it('computes SHA-256 of buffer', () => {
expect(sha256(Buffer.from('hello'))).toBe(
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
);
});
it('returns different SHAs for different inputs', () => {
expect(sha256('a')).not.toBe(sha256('b'));
});
});
// ───────────────────────────────────────────────────────────────────────────
// Boundary anchor SHAs match actual substrate at c9bda3d
// ───────────────────────────────────────────────────────────────────────────
describe('boundary anchor SHAs match substrate at c9bda3d', () => {
it('types.ts whole-file SHA matches pinned baseline', () => {
const actual = sha256File(TYPES_FILE);
expect(actual).toBe(BOUNDARY_SHAS.typesFile);
});
it('MULTI_STEP_ACTION_CONTRACT bytes SHA matches pinned baseline', () => {
const content = fs.readFileSync(TYPES_FILE, 'utf-8');
const bytes = extractMultiStepActionContractBytes(content);
expect(bytes).not.toBeNull();
expect(sha256(bytes!)).toBe(BOUNDARY_SHAS.multiStepActionContract);
});
it('extractMultiStepActionContractBytes captures the 252-byte constant', () => {
const content = fs.readFileSync(TYPES_FILE, 'utf-8');
const bytes = extractMultiStepActionContractBytes(content);
expect(bytes).not.toBeNull();
expect(Buffer.from(bytes!, 'utf-8').length).toBe(252);
expect(bytes!).toContain('Output exactly ONE JSON object on its own line');
});
it('extractMultiStepActionContractBytes returns null when constant absent', () => {
expect(extractMultiStepActionContractBytes('export const SOMETHING_ELSE = 42;')).toBeNull();
});
});
describe('all 5 baseline shape SHAs match substrate at c9bda3d', () => {
for (const shapeName of Object.keys(BASELINE_SHAPE_SHAS) as Array<keyof typeof BASELINE_SHAPE_SHAS>) {
it(`baseline ${shapeName} SHA matches pinned`, () => {
const filepath = path.join(PROMPT_SHAPES_DIR, shapeName);
const actual = sha256File(filepath);
expect(actual).toBe(BASELINE_SHAPE_SHAS[shapeName]);
});
}
});
// ───────────────────────────────────────────────────────────────────────────
// validateCandidate end-to-end
// ───────────────────────────────────────────────────────────────────────────
describe('validateCandidate — Gen 0 (baseline) acceptance', () => {
it('Gen 0 baseline shape passes validation with expectShapeDiff=false', () => {
const verdict = validateCandidate({
candidateShapeFilePath: path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'),
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: TYPES_FILE,
expectShapeDiff: false,
});
expect(verdict.valid).toBe(true);
expect(verdict.violations).toHaveLength(0);
expect(verdict.candidateShapeFileSha).toBe(BASELINE_SHAPE_SHAS['qwen-thinking.ts']);
});
it('Gen 0 baseline FAILS validation if expectShapeDiff=true (identity violation)', () => {
const verdict = validateCandidate({
candidateShapeFilePath: path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'),
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: TYPES_FILE,
expectShapeDiff: true,
});
expect(verdict.valid).toBe(false);
expect(verdict.violations.some(v => v.category === 'shape_file_unchanged_from_baseline')).toBe(true);
});
});
describe('validateCandidate — types.ts boundary violation detection', () => {
it('FAILS if types.ts SHA differs from pinned baseline', () => {
const tmpTypes = path.join(os.tmpdir(), `types-modified-${Date.now()}.ts`);
const original = fs.readFileSync(TYPES_FILE, 'utf-8');
fs.writeFileSync(tmpTypes, original + '\n// MODIFIED\n');
try {
const verdict = validateCandidate({
candidateShapeFilePath: path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'),
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: tmpTypes,
expectShapeDiff: false,
});
expect(verdict.valid).toBe(false);
expect(verdict.violations.some(v => v.category === 'types_file_modified')).toBe(true);
} finally {
fs.unlinkSync(tmpTypes);
}
});
it('FAILS if MULTI_STEP_ACTION_CONTRACT bytes are modified', () => {
const tmpTypes = path.join(os.tmpdir(), `types-contract-modified-${Date.now()}.ts`);
const original = fs.readFileSync(TYPES_FILE, 'utf-8');
const modified = original.replace(
'Output exactly ONE JSON object',
'Output exactly TWO JSON objects', // single-byte tweak in the contract
);
fs.writeFileSync(tmpTypes, modified);
try {
const verdict = validateCandidate({
candidateShapeFilePath: path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'),
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: tmpTypes,
expectShapeDiff: false,
});
expect(verdict.valid).toBe(false);
expect(verdict.violations.some(v => v.category === 'multi_step_action_contract_modified')).toBe(true);
} finally {
fs.unlinkSync(tmpTypes);
}
});
});
describe('validateCandidate — shape file metadata + imports validation', () => {
it('FAILS if locked metadata field is missing from candidate', () => {
const tmpShape = path.join(os.tmpdir(), `qwen-thinking-bad-${Date.now()}.ts`);
const original = fs.readFileSync(path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'), 'utf-8');
// Strip out modelClass: line entirely. Use \r?\n to handle both LF and CRLF
// line endings — git on Windows may check out files with CRLF.
const stripped = original.replace(/modelClass:.*\r?\n/, '');
expect(stripped).not.toContain('modelClass:'); // sanity: stripping actually worked
fs.writeFileSync(tmpShape, stripped);
try {
const verdict = validateCandidate({
candidateShapeFilePath: tmpShape,
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: TYPES_FILE,
expectShapeDiff: true,
});
expect(verdict.valid).toBe(false);
expect(verdict.violations.some(v => v.category === 'shape_file_metadata_locked_field_modified')).toBe(true);
} finally {
fs.unlinkSync(tmpShape);
}
});
it('FAILS if imports block is removed', () => {
const tmpShape = path.join(os.tmpdir(), `qwen-thinking-noimport-${Date.now()}.ts`);
const original = fs.readFileSync(path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'), 'utf-8');
const stripped = original.replace(/from '\.\/types\.js'/, "from './SOMETHING_ELSE.js'");
fs.writeFileSync(tmpShape, stripped);
try {
const verdict = validateCandidate({
candidateShapeFilePath: tmpShape,
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: TYPES_FILE,
expectShapeDiff: true,
});
expect(verdict.valid).toBe(false);
expect(verdict.violations.some(v => v.category === 'shape_file_imports_modified')).toBe(true);
} finally {
fs.unlinkSync(tmpShape);
}
});
it('PASSES if candidate uses ../types.js (gepa-evolved/ subdir convention)', () => {
const tmpShape = path.join(os.tmpdir(), `qwen-thinking-subdir-${Date.now()}.ts`);
const original = fs.readFileSync(path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'), 'utf-8');
// Simulate gepa-evolved/ subdir candidate: ./types.js → ../types.js, body mutation
const subdirImport = original
.replace(/from '\.\/types\.js'/, "from '../types.js'")
.replace('Answer the question precisely', 'Answer the question precisely (mutation)');
fs.writeFileSync(tmpShape, subdirImport);
try {
const verdict = validateCandidate({
candidateShapeFilePath: tmpShape,
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: TYPES_FILE,
expectShapeDiff: true,
});
expect(verdict.valid).toBe(true);
expect(verdict.violations.some(v => v.category === 'shape_file_imports_modified')).toBe(false);
} finally {
fs.unlinkSync(tmpShape);
}
});
});
describe('validateCandidate — accepts valid Gen 1 mutation', () => {
it('Gen 1 candidate with body-only mutation passes validation', () => {
const tmpShape = path.join(os.tmpdir(), `qwen-thinking-mutation-${Date.now()}.ts`);
const original = fs.readFileSync(path.join(PROMPT_SHAPES_DIR, 'qwen-thinking.ts'), 'utf-8');
// Realistic-shape mutation: change a string in the body, keep imports + metadata + structure
const mutated = original.replace(
'Answer the question precisely and substantively',
'Answer the question precisely, substantively, and with explicit retrieval',
);
expect(mutated).not.toBe(original); // sanity: mutation actually changed bytes
fs.writeFileSync(tmpShape, mutated);
try {
const verdict = validateCandidate({
candidateShapeFilePath: tmpShape,
baselineShapeName: 'qwen-thinking.ts',
typesFilePath: TYPES_FILE,
expectShapeDiff: true,
});
expect(verdict.valid).toBe(true);
expect(verdict.candidateShapeFileSha).not.toBe(BASELINE_SHAPE_SHAS['qwen-thinking.ts']);
} finally {
fs.unlinkSync(tmpShape);
}
});
});

View File

@@ -0,0 +1,154 @@
/**
* GEPA Faza 1 — REGISTRY-injection cross-module-boundary regression test.
*
* Per manifest v7 Amendment 8 §registry_invariant_test.
*
* Documents the H1 failure mode discovered via Gen 1 partial run b5avslp51 +
* diagnostic probe (benchmarks/gepa/scripts/faza-1/probe-registry-injection.ts):
*
* Under tsx + Node ESM with workspace path resolution, importing REGISTRY
* via a deep relative path produces a SEPARATE module instance from
* importing via the package path '@waggle/agent'. Mutations to one
* instance do NOT propagate to the other.
*
* This test asserts BOTH:
* (a) the failure mode (direct deep-path mutation does NOT propagate
* to package-import REGISTRY), so the bug class stays detectable
* if someone "fixes" the canonical path back to a deep import; AND
* (b) registerShape() via '@waggle/agent' DOES propagate to the
* agent-loop's view of REGISTRY (selectShape sees the new shape).
*
* If this test ever fails on (a) it means the underlying ESM resolver
* dedup-logic changed; if it ever fails on (b) it means registerShape
* was broken or its export path was changed.
*/
import { describe, expect, it } from 'vitest';
// Path A — deep relative import (the failing pattern from b5avslp51)
import { REGISTRY as RegistryFromScriptDeepPath } from '../../../../packages/agent/src/prompt-shapes/selector.js';
// Path B — package import (the canonical, agent-loop-equivalent path)
import {
REGISTRY as RegistryFromPackage,
registerShape,
selectShape,
type PromptShape,
} from '@waggle/agent';
function makeProbeShape(name: string): PromptShape {
// Minimal valid PromptShape stub — only structure matters for the registry test;
// method bodies are not invoked here.
return {
name,
metadata: {
description: `Probe shape ${name} for registry-injection regression test`,
modelClass: 'probe',
defaultThinking: false,
defaultMaxTokens: 100,
evidence_link: 'manifest v7 Amendment 8 §registry_invariant_test',
},
systemPrompt: () => 'probe',
soloUserPrompt: () => 'probe',
multiStepKickoffUserPrompt: () => 'probe',
retrievalInjectionUserPrompt: () => 'probe',
} as PromptShape;
}
describe('Amendment 8 §registry_invariant_test — REGISTRY cross-module-boundary', () => {
it('documents H1 failure mode: deep-relative-path REGISTRY and package REGISTRY are SEPARATE module instances', () => {
// This assertion documents the empirical finding from probe-registry-injection.ts
// run on 2026-04-28 (Node v22.19.0 + tsx). If the underlying ESM resolver ever
// deduplicates these paths, this test will fail-and-flag the change.
expect(RegistryFromScriptDeepPath).not.toBe(RegistryFromPackage);
});
it('documents H1: direct deep-path mutation does NOT propagate to package-import REGISTRY', () => {
const shapeName = 'amendment-8-h1-failure-mode-witness';
const shape = makeProbeShape(shapeName);
// Anti-pattern (the b5avslp51 bug): direct mutation via deep-path import.
(RegistryFromScriptDeepPath as Record<string, PromptShape>)[shapeName] = shape;
// Direct read on the same instance: visible.
expect(RegistryFromScriptDeepPath[shapeName]).toBe(shape);
// Read via the package-import (agent-loop's view): NOT visible.
expect(RegistryFromPackage[shapeName]).toBeUndefined();
// selectShape (from package, mirrors agent-loop call site): throws.
expect(() => selectShape('any-alias', { override: shapeName })).toThrow(
/not in REGISTRY/,
);
// Cleanup — remove the failure-mode witness so subsequent tests stay clean.
delete (RegistryFromScriptDeepPath as Record<string, PromptShape>)[shapeName];
});
it('FIX: registerShape() via @waggle/agent DOES propagate (canonical mutation API per §canonical_mutation_api)', () => {
const shapeName = 'amendment-8-canonical-fix-witness';
const shape = makeProbeShape(shapeName);
// Canonical mutation: registerShape imported from '@waggle/agent'.
registerShape(shapeName, shape);
// Read via the package-import (agent-loop's view): visible.
expect(RegistryFromPackage[shapeName]).toBe(shape);
// selectShape with override (matches agent-loop's selectShape call site).
const found = selectShape('any-alias', { override: shapeName });
expect(found).toBe(shape);
expect(found.name).toBe(shapeName);
// Cleanup — Faza 1 doesn't expose unregisterShape, so we mutate via canonical
// Path-B REGISTRY directly to keep cross-test isolation.
delete (RegistryFromPackage as Record<string, PromptShape>)[shapeName];
});
it('registerShape rejects empty name with informative error', () => {
const shape = makeProbeShape('temp');
expect(() => registerShape('', shape)).toThrow(/non-empty string/);
});
it('registerShape rejects malformed shape (missing systemPrompt method)', () => {
const malformed = {
name: 'malformed',
metadata: { description: 'nope', modelClass: 'x', defaultThinking: false, defaultMaxTokens: 1 },
// systemPrompt deliberately missing
} as unknown as PromptShape;
expect(() => registerShape('malformed-test', malformed)).toThrow(
/missing required PromptShape fields/,
);
});
it('registerShape registration survives multiple re-registrations (last-write-wins semantics)', () => {
const name = 'amendment-8-multi-register';
const shape1 = makeProbeShape(name);
const shape2 = makeProbeShape(name);
// Distinct identity but same name.
expect(shape1).not.toBe(shape2);
registerShape(name, shape1);
expect(selectShape('x', { override: name })).toBe(shape1);
registerShape(name, shape2); // re-register
expect(selectShape('x', { override: name })).toBe(shape2);
delete (RegistryFromPackage as Record<string, PromptShape>)[name];
});
it('registerShape via @waggle/agent makes the shape visible from listShapes()', async () => {
const name = 'amendment-8-listshapes-witness';
const shape = makeProbeShape(name);
const { listShapes } = await import('@waggle/agent');
const beforeNames = listShapes();
expect(beforeNames).not.toContain(name);
registerShape(name, shape);
const afterNames = listShapes();
expect(afterNames).toContain(name);
delete (RegistryFromPackage as Record<string, PromptShape>)[name];
});
});

View File

@@ -0,0 +1,263 @@
/**
* GEPA Faza 1 — acceptance validator tests.
*
* Coverage targets per manifest v7 §amendment_2_integration.scaffold_test_coverage_NEW_requirements
* mandatory_acceptance_tests:
* - §F.5 FAIL: Qwen candidate with trio_strict delta = +6pp AND mean retrieval = 1.4 → REJECTED
* - §F.5 PASS path: Qwen candidate with trio_strict delta = +6pp AND mean retrieval = 1.7 → ACCEPTED
*
* Plus comprehensive coverage of §F condition 1 (third update) for both Qwen and non-Qwen branches.
*/
import { describe, expect, it } from 'vitest';
import {
evaluateCandidate,
TRIO_STRICT_DELTA_THRESHOLD_PP,
QWEN_RETRIEVAL_ENGAGEMENT_FLOOR,
QWEN_FALSE_POSITIVE_RETRIEVAL_FLOOR,
} from '../../src/faza-1/acceptance.js';
import { type CandidateMetrics, type ShapeName } from '../../src/faza-1/types.js';
function makeCandidate(overrides: Partial<CandidateMetrics> & Pick<CandidateMetrics, 'shape'>): CandidateMetrics {
return {
candidateId: overrides.candidateId ?? `${overrides.shape}-test-candidate`,
shape: overrides.shape,
evaluations: overrides.evaluations ?? [],
trioStrictPassRateII: overrides.trioStrictPassRateII ?? 0.5,
trioStrictPassRateI: overrides.trioStrictPassRateI ?? 0.5,
meanRetrievalCallsPerTask: overrides.meanRetrievalCallsPerTask ?? 1.5,
meanCostUsd: overrides.meanCostUsd ?? 0.5,
};
}
// ───────────────────────────────────────────────────────────────────────────
// MANDATORY: §F.5 false-positive guard tests per Amendment 2 §5
// ───────────────────────────────────────────────────────────────────────────
describe('§F.5 false-positive evolution guard — mandatory Amendment 2 acceptance tests', () => {
it('Qwen candidate, trio_strict delta = +6pp, mean retrieval = 1.4 → REJECTED', () => {
// baseline = 0.20, candidate = 0.26 → delta = +6pp ≥ 5pp threshold
// retrieval = 1.4 < 1.5 false-positive floor → §F.5 triggers
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.26,
meanRetrievalCallsPerTask: 1.4,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.trioStrictDeltaPP).toBeCloseTo(6.0, 6);
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(true);
expect(verdict.accepted).toBe(false);
expect(verdict.reason).toContain('REJECTED §F.5 false-positive guard');
});
it('Qwen candidate, trio_strict delta = +6pp, mean retrieval = 1.7 → ACCEPTED', () => {
// baseline = 0.20, candidate = 0.26 → delta = +6pp
// retrieval = 1.7 ≥ 1.7 floor (engagement gap closed) → §F.5 does NOT trigger
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.26,
meanRetrievalCallsPerTask: 1.7,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.trioStrictDeltaPP).toBeCloseTo(6.0, 6);
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(false);
expect(verdict.condition1Pass).toBe(true);
expect(verdict.accepted).toBe(true);
expect(verdict.reason).toContain('PASS §F.1');
});
it('§F.5 boundary: retrieval = 1.49 → REJECTED (just below floor)', () => {
const candidate = makeCandidate({
shape: 'qwen-non-thinking',
trioStrictPassRateII: 0.30, // delta = +10pp
meanRetrievalCallsPerTask: 1.49,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(true);
expect(verdict.accepted).toBe(false);
});
it('§F.5 boundary: retrieval = 1.50 → §F.5 NOT triggered (exact floor inclusive)', () => {
// 1.50 ≥ 1.50 false-positive floor → guard does not fire
// But 1.50 < 1.70 §F.1 Qwen floor → condition 1 still fails on different criterion
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.30, // delta = +10pp
meanRetrievalCallsPerTask: 1.50,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(false);
expect(verdict.condition1Pass).toBe(false); // fails Qwen retrieval floor 1.7
expect(verdict.accepted).toBe(false);
expect(verdict.reason).toContain('FAIL §F.1 Qwen retrieval floor');
});
it('§F.5 does NOT trigger when delta < threshold even if retrieval low (Qwen)', () => {
// delta = +3pp < 5pp threshold → guard not even evaluated
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.23,
meanRetrievalCallsPerTask: 1.0, // low, but delta below threshold
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(false);
expect(verdict.condition1Pass).toBe(false);
expect(verdict.accepted).toBe(false);
expect(verdict.reason).toContain('FAIL §F.1 trio_strict delta');
});
});
// ───────────────────────────────────────────────────────────────────────────
// §F.5 does NOT apply to non-Qwen shapes
// ───────────────────────────────────────────────────────────────────────────
describe('§F.5 scoping — only applies to Qwen-targeted shapes', () => {
it('claude shape with delta = +6pp + retrieval = 0.5 → ACCEPTED (no false-positive guard)', () => {
const candidate = makeCandidate({
shape: 'claude',
trioStrictPassRateII: 0.26,
meanRetrievalCallsPerTask: 0.5, // would trigger §F.5 if Qwen, but doesn't apply here
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(false);
expect(verdict.condition1Pass).toBe(true);
expect(verdict.accepted).toBe(true);
});
it('gpt shape with delta = +5pp + retrieval = 0 → ACCEPTED', () => {
const candidate = makeCandidate({
shape: 'gpt',
trioStrictPassRateII: 0.25,
meanRetrievalCallsPerTask: 0,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(false);
expect(verdict.accepted).toBe(true);
});
it('generic-simple shape: only trio_strict delta matters, no retrieval requirement', () => {
const candidate = makeCandidate({
shape: 'generic-simple',
trioStrictPassRateII: 0.30,
meanRetrievalCallsPerTask: 1.0,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.accepted).toBe(true);
});
});
// ───────────────────────────────────────────────────────────────────────────
// §F condition 1 trio_strict delta — basic threshold tests
// ───────────────────────────────────────────────────────────────────────────
describe('§F condition 1 — trio_strict delta threshold (≥+5pp)', () => {
it('delta = +5pp exactly (boundary inclusive) → condition 1 PASS for non-Qwen', () => {
const candidate = makeCandidate({
shape: 'claude',
trioStrictPassRateII: 0.25,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.trioStrictDeltaPP).toBeCloseTo(5.0, 6);
expect(verdict.condition1Pass).toBe(true);
expect(verdict.accepted).toBe(true);
});
it('delta = +4.99pp (just below boundary) → condition 1 FAIL', () => {
const candidate = makeCandidate({
shape: 'claude',
trioStrictPassRateII: 0.2499,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition1Pass).toBe(false);
expect(verdict.accepted).toBe(false);
});
it('negative delta (regression) → condition 1 FAIL', () => {
const candidate = makeCandidate({
shape: 'gpt',
trioStrictPassRateII: 0.10,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.trioStrictDeltaPP).toBeCloseTo(-10.0, 6);
expect(verdict.condition1Pass).toBe(false);
});
it('exposes binding constants for external auditing', () => {
expect(TRIO_STRICT_DELTA_THRESHOLD_PP).toBe(5);
expect(QWEN_RETRIEVAL_ENGAGEMENT_FLOOR).toBe(1.7);
expect(QWEN_FALSE_POSITIVE_RETRIEVAL_FLOOR).toBe(1.5);
});
});
// ───────────────────────────────────────────────────────────────────────────
// §F condition 1 — Qwen-only retrieval engagement floor (1.7)
// ───────────────────────────────────────────────────────────────────────────
describe('§F condition 1 — Qwen retrieval engagement floor (1.7) sub-criterion', () => {
it('Qwen candidate, delta = +5pp, retrieval = 1.69 → FAIL (below 1.7 floor)', () => {
// 1.69 ≥ 1.5 false-positive floor (so §F.5 does not trigger)
// but 1.69 < 1.7 §F.1 Qwen floor (so condition 1 fails)
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.25,
meanRetrievalCallsPerTask: 1.69,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition5FalsePositiveGuardTriggered).toBe(false);
expect(verdict.condition1Pass).toBe(false);
expect(verdict.accepted).toBe(false);
expect(verdict.reason).toContain('FAIL §F.1 Qwen retrieval floor');
});
it('Qwen candidate, delta = +5pp, retrieval = 1.70 → PASS (exact floor inclusive)', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.25,
meanRetrievalCallsPerTask: 1.70,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.condition1Pass).toBe(true);
expect(verdict.accepted).toBe(true);
});
it('Qwen candidate, delta = +5pp, retrieval = 2.5 → PASS (well above floor)', () => {
const candidate = makeCandidate({
shape: 'qwen-non-thinking',
trioStrictPassRateII: 0.25,
meanRetrievalCallsPerTask: 2.5,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.accepted).toBe(true);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Audit log invariants
// ───────────────────────────────────────────────────────────────────────────
describe('audit log invariants', () => {
it('reason string includes shape, delta, retrieval, accepted flag', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.30,
meanRetrievalCallsPerTask: 2.0,
});
const verdict = evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.20 });
expect(verdict.reason).toContain('shape=qwen-thinking');
expect(verdict.reason).toContain('trio_strict_delta=10.00pp');
expect(verdict.reason).toContain('mean_retrieval_calls=2.00');
expect(verdict.reason).toContain('accepted=true');
});
it('every shape produces a verdict (no exceptions)', () => {
const shapes: ShapeName[] = ['claude', 'qwen-thinking', 'qwen-non-thinking', 'gpt', 'generic-simple'];
for (const shape of shapes) {
const candidate = makeCandidate({ shape, trioStrictPassRateII: 0.5 });
expect(() =>
evaluateCandidate({ candidate, baselineTrioStrictPassRateII: 0.4 }),
).not.toThrow();
}
});
});

View File

@@ -0,0 +1,349 @@
/**
* GEPA Faza 1 — H3 corpus library tests.
*
* Coverage targets per manifest v7 §corpus_design + §amendment_2_integration:
* - Stratification: exactly 50 unique cells (5 × 5 × 2)
* - Deterministic enumeration order (canonical)
* - Instance validation per quality floor
* - Spot-audit sampler determinism (same seed → same sample)
* - Spot-audit halt-on-failure semantics
*/
import { describe, expect, it } from 'vitest';
import {
TASK_FAMILIES,
PERSONAS,
COMPANY_STAGES,
TOTAL_INSTANCES,
DOCS_PER_INSTANCE_MIN,
DOCS_PER_INSTANCE_MAX,
SPOT_AUDIT_SAMPLE_SIZE,
STRATIFICATION_SEED,
TASK_FAMILY_DESCRIPTORS,
type StratificationCell,
type CorpusInstance,
iterateStratificationCells,
listStratificationCells,
buildInstanceId,
validateInstance,
deterministicSample,
selectSpotAuditSample,
runSpotAudit,
corpusSha256,
} from '../../src/faza-1/corpus.js';
// ───────────────────────────────────────────────────────────────────────────
// Helpers
// ───────────────────────────────────────────────────────────────────────────
function makeValidInstance(cell: StratificationCell, ordinal: number = 1): CorpusInstance {
const docs = Array.from({ length: 7 }, (_, i) => ({
title: `DOC ${i + 1} — Sample`,
body: 'x'.repeat(800),
charCount: 800,
}));
return {
instanceId: buildInstanceId(cell, ordinal),
cell,
personaText: 'p'.repeat(200),
scenario: 's'.repeat(400),
sourceDocuments: docs,
question: 'q'.repeat(200),
materialsConcat: docs.map(d => `## ${d.title}\n\n${d.body}`).join('\n\n---\n\n'),
manifestAnchor: 'manifest-v7-gepa-faza1',
generatedBy: 'claude-opus-4-7',
generatedAtIso: '2026-04-28T00:00:00.000Z',
generationCostUsd: 0.10,
};
}
// ───────────────────────────────────────────────────────────────────────────
// Constants exposed for auditing
// ───────────────────────────────────────────────────────────────────────────
describe('manifest v7 §corpus_design constants', () => {
it('TOTAL_INSTANCES = 50', () => {
expect(TOTAL_INSTANCES).toBe(50);
});
it('TASK_FAMILIES has 5 entries', () => {
expect(TASK_FAMILIES).toHaveLength(5);
});
it('PERSONAS has 5 entries', () => {
expect(PERSONAS).toHaveLength(5);
});
it('COMPANY_STAGES has 2 entries', () => {
expect(COMPANY_STAGES).toHaveLength(2);
});
it('5 × 5 × 2 = 50 (stratification yields TOTAL_INSTANCES)', () => {
expect(TASK_FAMILIES.length * PERSONAS.length * COMPANY_STAGES.length).toBe(TOTAL_INSTANCES);
});
it('SPOT_AUDIT_SAMPLE_SIZE = 5 per Amendment 1', () => {
expect(SPOT_AUDIT_SAMPLE_SIZE).toBe(5);
});
it('STRATIFICATION_SEED = 42', () => {
expect(STRATIFICATION_SEED).toBe(42);
});
it('DOCS_PER_INSTANCE bounds', () => {
expect(DOCS_PER_INSTANCE_MIN).toBe(6);
expect(DOCS_PER_INSTANCE_MAX).toBe(8);
});
it('TASK_FAMILY_DESCRIPTORS covers all 5 families', () => {
for (const f of TASK_FAMILIES) {
expect(TASK_FAMILY_DESCRIPTORS[f]).toBeDefined();
expect(TASK_FAMILY_DESCRIPTORS[f].docsPerInstance).toBeGreaterThanOrEqual(DOCS_PER_INSTANCE_MIN);
expect(TASK_FAMILY_DESCRIPTORS[f].docsPerInstance).toBeLessThanOrEqual(DOCS_PER_INSTANCE_MAX);
}
});
it('F1-F3 mirror pilot tasks; F4-F5 are net-new', () => {
expect(TASK_FAMILY_DESCRIPTORS.F1.mirrorPilotTask).toBe('task-1');
expect(TASK_FAMILY_DESCRIPTORS.F2.mirrorPilotTask).toBe('task-2');
expect(TASK_FAMILY_DESCRIPTORS.F3.mirrorPilotTask).toBe('task-3');
expect(TASK_FAMILY_DESCRIPTORS.F4.mirrorPilotTask).toBeNull();
expect(TASK_FAMILY_DESCRIPTORS.F5.mirrorPilotTask).toBeNull();
});
});
// ───────────────────────────────────────────────────────────────────────────
// Stratification enumeration
// ───────────────────────────────────────────────────────────────────────────
describe('iterateStratificationCells / listStratificationCells', () => {
it('yields exactly 50 cells (5 × 5 × 2)', () => {
const cells = listStratificationCells();
expect(cells).toHaveLength(50);
});
it('all 50 cells are unique', () => {
const cells = listStratificationCells();
const keys = new Set(cells.map(c => `${c.family}|${c.persona}|${c.stage}`));
expect(keys.size).toBe(50);
});
it('canonical ordering: F1 first, F5 last', () => {
const cells = listStratificationCells();
expect(cells[0].family).toBe('F1');
expect(cells[cells.length - 1].family).toBe('F5');
});
it('persona enumeration is the inner loop after family', () => {
const cells = listStratificationCells();
// First 10 cells should all be family F1
for (let i = 0; i < 10; i++) {
expect(cells[i].family).toBe('F1');
}
// Cells 10-19 should all be F2
for (let i = 10; i < 20; i++) {
expect(cells[i].family).toBe('F2');
}
});
it('each (family, persona) pair appears exactly twice (once per stage)', () => {
const cells = listStratificationCells();
const pairCounts = new Map<string, number>();
for (const c of cells) {
const key = `${c.family}|${c.persona}`;
pairCounts.set(key, (pairCounts.get(key) ?? 0) + 1);
}
expect(pairCounts.size).toBe(25); // 5 × 5 family-persona pairs
for (const count of pairCounts.values()) {
expect(count).toBe(2); // once per stage
}
});
it('iterator is deterministic (same yield order across calls)', () => {
const a = listStratificationCells();
const b = listStratificationCells();
expect(a).toEqual(b);
});
});
// ───────────────────────────────────────────────────────────────────────────
// buildInstanceId
// ───────────────────────────────────────────────────────────────────────────
describe('buildInstanceId', () => {
it('produces stable IDs with h3- prefix + zero-padded ordinal', () => {
const id = buildInstanceId({ family: 'F1', persona: 'p2_cfo', stage: 'stage_a_series_b_growth_burning' }, 7);
expect(id).toBe('h3-F1-p2_cfo-stage_a_series_b_growth_burning-007');
});
it('default ordinal is 1', () => {
const id = buildInstanceId({ family: 'F3', persona: 'p4_vp_finance', stage: 'stage_b_post_profitable_consolidation' });
expect(id).toContain('-001');
});
});
// ───────────────────────────────────────────────────────────────────────────
// validateInstance
// ───────────────────────────────────────────────────────────────────────────
describe('validateInstance — quality floor', () => {
it('valid synthetic instance passes', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
const r = validateInstance(inst);
expect(r.valid).toBe(true);
expect(r.violations).toHaveLength(0);
});
it('FAIL: too few docs', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.sourceDocuments = inst.sourceDocuments.slice(0, 3);
const r = validateInstance(inst);
expect(r.valid).toBe(false);
expect(r.violations[0]).toContain('docs count');
expect(r.violations[0]).toContain('< 6 min');
});
it('FAIL: too many docs', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.sourceDocuments = [...inst.sourceDocuments, ...inst.sourceDocuments, ...inst.sourceDocuments];
const r = validateInstance(inst);
expect(r.valid).toBe(false);
expect(r.violations[0]).toContain('> 8 max');
});
it('FAIL: persona too short', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.personaText = 'short';
const r = validateInstance(inst);
expect(r.valid).toBe(false);
expect(r.violations.some(v => v.includes('persona length'))).toBe(true);
});
it('PASS: rich persona up to 1500 chars (post-probe loosening)', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.personaText = 'p'.repeat(1400);
const r = validateInstance(inst);
expect(r.valid).toBe(true);
});
it('FAIL: persona above 1500 cap', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.personaText = 'p'.repeat(1600);
const r = validateInstance(inst);
expect(r.valid).toBe(false);
expect(r.violations.some(v => v.includes('persona length'))).toBe(true);
});
it('PASS: empty scenario (oracle embedded it in personaText)', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.scenario = ''; // embedded case
const r = validateInstance(inst);
expect(r.valid).toBe(true);
});
it('FAIL: doc charCount mismatch with body length', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.sourceDocuments[0].charCount = 999; // intentional mismatch
const r = validateInstance(inst);
expect(r.valid).toBe(false);
expect(r.violations.some(v => v.includes('charCount'))).toBe(true);
});
it('FAIL: instanceId missing h3- prefix', () => {
const inst = makeValidInstance({ family: 'F1', persona: 'p1_founder_ceo', stage: 'stage_a_series_b_growth_burning' });
inst.instanceId = 'wrong-prefix-001';
const r = validateInstance(inst);
expect(r.valid).toBe(false);
expect(r.violations.some(v => v.includes('h3- prefix'))).toBe(true);
});
});
// ───────────────────────────────────────────────────────────────────────────
// deterministicSample / selectSpotAuditSample
// ───────────────────────────────────────────────────────────────────────────
describe('deterministicSample', () => {
it('returns sample of requested size', () => {
const items = Array.from({ length: 50 }, (_, i) => `item-${i}`);
const sample = deterministicSample(items, 5);
expect(sample).toHaveLength(5);
});
it('same seed produces identical sample (reproducibility)', () => {
const items = Array.from({ length: 50 }, (_, i) => `item-${i}`);
const a = deterministicSample(items, 5, 42);
const b = deterministicSample(items, 5, 42);
expect(a).toEqual(b);
});
it('different seeds produce different samples', () => {
const items = Array.from({ length: 50 }, (_, i) => `item-${i}`);
const a = deterministicSample(items, 5, 42);
const c = deterministicSample(items, 5, 100);
expect(a).not.toEqual(c);
});
it('sampleSize >= items.length returns full list copy', () => {
const items = ['a', 'b', 'c'];
const sample = deterministicSample(items, 5);
expect(sample).toHaveLength(3);
expect(sample).toEqual(items);
expect(sample).not.toBe(items); // copy, not reference
});
});
describe('selectSpotAuditSample', () => {
it('returns 5 instances by default (manifest v7 spot_audit.sample_size)', () => {
const cells = listStratificationCells();
const instances = cells.map(c => makeValidInstance(c));
const sample = selectSpotAuditSample(instances);
expect(sample).toHaveLength(5);
});
it('reproducible with seed=42 (same instances picked across runs)', () => {
const cells = listStratificationCells();
const instances = cells.map(c => makeValidInstance(c));
const a = selectSpotAuditSample(instances);
const b = selectSpotAuditSample(instances);
expect(a.map(i => i.instanceId)).toEqual(b.map(i => i.instanceId));
});
});
// ───────────────────────────────────────────────────────────────────────────
// runSpotAudit aggregate
// ───────────────────────────────────────────────────────────────────────────
describe('runSpotAudit', () => {
it('PASS when all sampled instances valid', () => {
const cells = listStratificationCells();
const instances = cells.map(c => makeValidInstance(c));
const report = runSpotAudit(instances);
expect(report.sampleSize).toBe(5);
expect(report.haltOnFailure).toBe(false);
expect(report.haltReason).toBeUndefined();
});
it('HALT when any sampled instance invalid (manifest v7 spot_audit.halt_on)', () => {
const cells = listStratificationCells();
const instances = cells.map(c => makeValidInstance(c));
// Corrupt every instance (so sample will definitely include corrupted ones)
for (const inst of instances) {
inst.sourceDocuments = inst.sourceDocuments.slice(0, 2); // below 6 min
}
const report = runSpotAudit(instances);
expect(report.haltOnFailure).toBe(true);
expect(report.haltReason).toMatch(/spot-audit instances failed validation/);
});
});
// ───────────────────────────────────────────────────────────────────────────
// corpusSha256
// ───────────────────────────────────────────────────────────────────────────
describe('corpusSha256', () => {
it('produces deterministic SHA across runs', () => {
const cells = listStratificationCells().slice(0, 5);
const instances = cells.map(c => makeValidInstance(c));
const a = corpusSha256(instances);
const b = corpusSha256(instances);
expect(a).toBe(b);
});
it('different corpora produce different SHAs', () => {
const cells = listStratificationCells();
const a = cells.slice(0, 5).map(c => makeValidInstance(c));
const b = cells.slice(5, 10).map(c => makeValidInstance(c));
expect(corpusSha256(a)).not.toBe(corpusSha256(b));
});
});

View File

@@ -0,0 +1,180 @@
/**
* GEPA Faza 1 — cost tracker tests.
*
* Coverage targets:
* - Halt triggers: HARD_CAP_USD_BREACH ($100), INTERNAL_HALT_USD_BREACH ($80),
* SUPER_LINEAR_PROJECTION_BREACH (>30% over expected)
* - Audit cadence (every 20 evaluations)
* - Immutable state updates per coding-style.md
* - Projection multiplier 1.5× per brief §6.7
*/
import { describe, expect, it } from 'vitest';
import {
HARD_CAP_USD,
INTERNAL_HALT_USD,
SUPER_LINEAR_MULTIPLIER,
SUPER_LINEAR_OVERAGE_THRESHOLD,
AUDIT_CADENCE_EVAL_COUNT,
createCostTracker,
recordEvaluation,
checkHaltTriggers,
shouldAudit,
} from '../../src/faza-1/cost-tracker.js';
describe('constants exposed for auditing', () => {
it('HARD_CAP_USD = $100', () => {
expect(HARD_CAP_USD).toBe(100.0);
});
it('INTERNAL_HALT_USD = $80', () => {
expect(INTERNAL_HALT_USD).toBe(80.0);
});
it('SUPER_LINEAR_MULTIPLIER = 1.5 per brief §6.7', () => {
expect(SUPER_LINEAR_MULTIPLIER).toBe(1.5);
});
it('SUPER_LINEAR_OVERAGE_THRESHOLD = 0.30 (30%)', () => {
expect(SUPER_LINEAR_OVERAGE_THRESHOLD).toBe(0.30);
});
it('AUDIT_CADENCE_EVAL_COUNT = 20 per launch decision §A.7', () => {
expect(AUDIT_CADENCE_EVAL_COUNT).toBe(20);
});
});
describe('createCostTracker', () => {
it('initializes with zero spend + projection = baseline × 1.5', () => {
const t = createCostTracker(0.50);
expect(t.cumulativeUsd).toBe(0);
expect(t.evaluationCount).toBe(0);
expect(t.projectionPerEvalUsd).toBe(0.75); // 0.50 × 1.5
});
});
describe('recordEvaluation — immutable updates', () => {
it('returns new state object (does not mutate input)', () => {
const before = createCostTracker(0.50);
const after = recordEvaluation(before, 0.30);
expect(before.cumulativeUsd).toBe(0);
expect(before.evaluationCount).toBe(0);
expect(after.cumulativeUsd).toBe(0.30);
expect(after.evaluationCount).toBe(1);
expect(before).not.toBe(after);
});
it('cumulative cost accumulates across multiple recordings', () => {
let s = createCostTracker(0.50);
s = recordEvaluation(s, 0.40);
s = recordEvaluation(s, 0.60);
s = recordEvaluation(s, 0.50);
expect(s.cumulativeUsd).toBeCloseTo(1.50, 6);
expect(s.evaluationCount).toBe(3);
});
});
describe('checkHaltTriggers — HARD_CAP_USD_BREACH', () => {
it('triggers at $100.01', () => {
const s = { cumulativeUsd: 100.01, evaluationCount: 200, projectionPerEvalUsd: 0.50 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('HARD_CAP_USD_BREACH');
expect(r.message).toContain('HARD CAP BREACH');
});
it('does NOT trigger at $100.00 exactly (boundary inclusive of pass)', () => {
const s = { cumulativeUsd: 100.00, evaluationCount: 200, projectionPerEvalUsd: 0.50 };
const r = checkHaltTriggers(s);
expect(r.haltReason).not.toBe('HARD_CAP_USD_BREACH');
// It will trigger INTERNAL_HALT since $100 > $80, but not HARD_CAP
expect(r.haltReason).toBe('INTERNAL_HALT_USD_BREACH');
});
it('takes precedence over INTERNAL_HALT (most severe first)', () => {
const s = { cumulativeUsd: 105.0, evaluationCount: 200, projectionPerEvalUsd: 0.50 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('HARD_CAP_USD_BREACH');
});
});
describe('checkHaltTriggers — INTERNAL_HALT_USD_BREACH', () => {
it('triggers at $80.01', () => {
const s = { cumulativeUsd: 80.01, evaluationCount: 160, projectionPerEvalUsd: 0.50 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('INTERNAL_HALT_USD_BREACH');
});
it('does NOT trigger at $80.00 exactly', () => {
const s = { cumulativeUsd: 80.00, evaluationCount: 160, projectionPerEvalUsd: 0.50 };
const r = checkHaltTriggers(s);
expect(r.haltReason).not.toBe('INTERNAL_HALT_USD_BREACH');
});
});
describe('checkHaltTriggers — SUPER_LINEAR_PROJECTION_BREACH', () => {
it('triggers when actual exceeds expected by >30%', () => {
// 10 evals × $0.75/eval projection = $7.50 expected; actual $10 = 33% over
const s = { cumulativeUsd: 10.0, evaluationCount: 10, projectionPerEvalUsd: 0.75 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('SUPER_LINEAR_PROJECTION_BREACH');
expect(r.overageFraction).toBeCloseTo(0.333, 2);
});
it('does NOT trigger when actual is exactly at projection', () => {
const s = { cumulativeUsd: 7.50, evaluationCount: 10, projectionPerEvalUsd: 0.75 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('NONE');
expect(r.overageFraction).toBe(0);
});
it('does NOT trigger when overage is exactly at 30% threshold (boundary inclusive of pass)', () => {
// Expected $7.50, actual $9.75 = 30% over exactly
const s = { cumulativeUsd: 9.75, evaluationCount: 10, projectionPerEvalUsd: 0.75 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('NONE');
});
it('does NOT trigger before any eval recorded (no expected baseline)', () => {
const s = { cumulativeUsd: 0, evaluationCount: 0, projectionPerEvalUsd: 0.75 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('NONE');
});
it('takes precedence below INTERNAL_HALT (super-linear can fire while still under $80)', () => {
// Expected $1.50 at 2 evals × $0.75; actual $5 = 233% over → super-linear breach
const s = { cumulativeUsd: 5.0, evaluationCount: 2, projectionPerEvalUsd: 0.75 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('SUPER_LINEAR_PROJECTION_BREACH');
expect(r.cumulativeUsd).toBeLessThan(INTERNAL_HALT_USD);
});
});
describe('shouldAudit — audit cadence', () => {
it('returns false at evaluationCount = 0', () => {
expect(shouldAudit({ cumulativeUsd: 0, evaluationCount: 0, projectionPerEvalUsd: 0.5 })).toBe(false);
});
it('returns true at evaluationCount = 20 (first audit boundary)', () => {
expect(shouldAudit({ cumulativeUsd: 10, evaluationCount: 20, projectionPerEvalUsd: 0.5 })).toBe(true);
});
it('returns true at every multiple of 20', () => {
for (const n of [40, 60, 80, 100, 200]) {
expect(shouldAudit({ cumulativeUsd: n / 2, evaluationCount: n, projectionPerEvalUsd: 0.5 })).toBe(true);
}
});
it('returns false at non-boundary counts', () => {
for (const n of [1, 5, 19, 21, 39, 99]) {
expect(shouldAudit({ cumulativeUsd: n / 2, evaluationCount: n, projectionPerEvalUsd: 0.5 })).toBe(false);
}
});
});
describe('end-to-end Faza 1 cost projection', () => {
it('expected total $100.50 reaches HARD_CAP_USD_BREACH (Amendment 1 §4 tight margin)', () => {
// Simulate Faza 1 expected breakdown reaching $100.50
let s = createCostTracker(0.50); // baseline $0.50/eval
// 50 corpus + 40 NULL + 120 Gen 1 + 25 held-out = 235 evaluations × roughly $0.43/eval
// For test purposes just simulate hitting $100.50
s = { ...s, cumulativeUsd: 100.50, evaluationCount: 235 };
const r = checkHaltTriggers(s);
expect(r.haltReason).toBe('HARD_CAP_USD_BREACH');
});
});

View File

@@ -0,0 +1,595 @@
/**
* GEPA Faza 1 — fitness function tests.
*
* Coverage targets per manifest v7 §amendment_2_integration.scaffold_test_coverage_NEW_requirements:
* - 5 retrieval engagement boundary cases (1.49 / 1.50 / 1.99 / 2.00 / 2.50)
* - 5 shape-routing tests (claude/gpt/generic-simple excluded; qwen-thinking/qwen-non-thinking included)
* - Cost penalty: zero overage + positive overage scenarios
* - End-to-end computeFitness invariants on both Qwen and non-Qwen shapes
*
* §F.5 false-positive guard tests live in acceptance.test.ts (separate module).
*/
import { describe, expect, it } from 'vitest';
import {
computeRetrievalEngagementBonus,
computeCostPenalty,
computeFitness,
RETRIEVAL_ENGAGEMENT_BANDS,
computeTier2RetrievalBonus,
computeTieredFitness,
computeDeltaFloorVerdict,
TIER_2_BONUS_CAP,
TIER_2_BONUS_PER_PP,
TIER_3_BONUS_FULL_INVARIANCE,
TIER_3_ANCHOR_COUNT_FULL,
DELTA_FLOOR_THRESHOLDS,
} from '../../src/faza-1/fitness.js';
import {
type CandidateMetrics,
type ShapeName,
QWEN_TARGETED_SHAPES,
NON_QWEN_SHAPES,
NULL_BASELINE_PER_SHAPE,
NULL_BASELINE_AGGREGATE,
} from '../../src/faza-1/types.js';
// ───────────────────────────────────────────────────────────────────────────
// Helpers
// ───────────────────────────────────────────────────────────────────────────
function makeCandidate(overrides: Partial<CandidateMetrics> & Pick<CandidateMetrics, 'shape'>): CandidateMetrics {
return {
candidateId: overrides.candidateId ?? `${overrides.shape}-test-candidate`,
shape: overrides.shape,
evaluations: overrides.evaluations ?? [],
trioStrictPassRateII: overrides.trioStrictPassRateII ?? 0.5,
trioStrictPassRateI: overrides.trioStrictPassRateI ?? 0.5,
meanRetrievalCallsPerTask: overrides.meanRetrievalCallsPerTask ?? 1.5,
meanCostUsd: overrides.meanCostUsd ?? 0.5,
};
}
// ───────────────────────────────────────────────────────────────────────────
// computeRetrievalEngagementBonus — Amendment 2 §3 binding boundary tests
// ───────────────────────────────────────────────────────────────────────────
describe('computeRetrievalEngagementBonus — Amendment 2 §3 boundary cases', () => {
// Per manifest v7 §amendment_2_integration.scaffold_test_coverage_NEW_requirements
// mandatory_boundary_tests block — these 5 cases are BINDING contract tests.
it('qwen-thinking, mean retrieval_calls = 1.49 → expect bonus = -0.05', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 1.49)).toBe(-0.05);
});
it('qwen-thinking, mean retrieval_calls = 1.50 → expect bonus = 0.00 (lower threshold inclusive)', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 1.50)).toBe(0.0);
});
it('qwen-thinking, mean retrieval_calls = 1.99 → expect bonus = 0.00', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 1.99)).toBe(0.0);
});
it('qwen-thinking, mean retrieval_calls = 2.00 → expect bonus = +0.05 (upper threshold inclusive)', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 2.00)).toBe(0.05);
});
it('qwen-thinking, mean retrieval_calls = 2.50 → expect bonus = +0.05', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 2.50)).toBe(0.05);
});
// Symmetry check on qwen-non-thinking (other Qwen-targeted shape)
it('qwen-non-thinking exhibits identical band behavior to qwen-thinking', () => {
expect(computeRetrievalEngagementBonus('qwen-non-thinking', 1.49)).toBe(-0.05);
expect(computeRetrievalEngagementBonus('qwen-non-thinking', 1.50)).toBe(0.0);
expect(computeRetrievalEngagementBonus('qwen-non-thinking', 2.00)).toBe(0.05);
});
// Edge cases beyond the 5 mandatory boundaries — defensive coverage
it('handles 0 retrieval calls (extreme low)', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 0)).toBe(-0.05);
});
it('handles very high retrieval calls (extreme high)', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 10.0)).toBe(0.05);
});
it('exposes binding band constants for external auditing', () => {
expect(RETRIEVAL_ENGAGEMENT_BANDS.upperThreshold).toBe(2.0);
expect(RETRIEVAL_ENGAGEMENT_BANDS.lowerThreshold).toBe(1.5);
expect(RETRIEVAL_ENGAGEMENT_BANDS.bonusPlus).toBe(0.05);
expect(RETRIEVAL_ENGAGEMENT_BANDS.bonusZero).toBe(0.0);
expect(RETRIEVAL_ENGAGEMENT_BANDS.bonusMinus).toBe(-0.05);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Shape-routing tests — Amendment 2 §3 mandatory_routing_tests
// ───────────────────────────────────────────────────────────────────────────
describe('shape-routing — Amendment 2 §3 retrieval engagement excluded for non-Qwen shapes', () => {
// Per Amendment 2 §3 rationale: "Phase 4.5 finding is Qwen-specific. Opus
// shape does NOT have the gap. Applying retrieval-engagement bonus uniformly
// across all shapes would distort fitness for shapes that don't have the
// underlying behavioral problem."
it('claude shape: bonus computation NOT applied (excluded)', () => {
expect(computeRetrievalEngagementBonus('claude', 0)).toBe(0.0);
expect(computeRetrievalEngagementBonus('claude', 1.49)).toBe(0.0);
expect(computeRetrievalEngagementBonus('claude', 2.50)).toBe(0.0);
expect(computeRetrievalEngagementBonus('claude', 100)).toBe(0.0);
});
it('gpt shape: bonus computation NOT applied (excluded)', () => {
expect(computeRetrievalEngagementBonus('gpt', 0)).toBe(0.0);
expect(computeRetrievalEngagementBonus('gpt', 1.49)).toBe(0.0);
expect(computeRetrievalEngagementBonus('gpt', 2.50)).toBe(0.0);
});
it('generic-simple shape: bonus computation NOT applied (excluded)', () => {
expect(computeRetrievalEngagementBonus('generic-simple', 0)).toBe(0.0);
expect(computeRetrievalEngagementBonus('generic-simple', 1.49)).toBe(0.0);
expect(computeRetrievalEngagementBonus('generic-simple', 2.50)).toBe(0.0);
});
it('qwen-thinking shape: bonus computation IS applied', () => {
expect(computeRetrievalEngagementBonus('qwen-thinking', 1.49)).toBe(-0.05);
expect(computeRetrievalEngagementBonus('qwen-thinking', 2.50)).toBe(0.05);
});
it('qwen-non-thinking shape: bonus computation IS applied', () => {
expect(computeRetrievalEngagementBonus('qwen-non-thinking', 1.49)).toBe(-0.05);
expect(computeRetrievalEngagementBonus('qwen-non-thinking', 2.50)).toBe(0.05);
});
it('shape-class set membership matches manifest v7 declaration', () => {
// Manifest v7 §metric_operationalization.retrieval_engagement_bonus.applies_to_shapes
expect(QWEN_TARGETED_SHAPES.has('qwen-thinking')).toBe(true);
expect(QWEN_TARGETED_SHAPES.has('qwen-non-thinking')).toBe(true);
expect(QWEN_TARGETED_SHAPES.has('claude')).toBe(false);
expect(QWEN_TARGETED_SHAPES.has('gpt')).toBe(false);
expect(QWEN_TARGETED_SHAPES.has('generic-simple')).toBe(false);
// Manifest v7 §metric_operationalization.retrieval_engagement_bonus.excluded_shapes
expect(NON_QWEN_SHAPES.has('claude')).toBe(true);
expect(NON_QWEN_SHAPES.has('gpt')).toBe(true);
expect(NON_QWEN_SHAPES.has('generic-simple')).toBe(true);
expect(NON_QWEN_SHAPES.has('qwen-thinking')).toBe(false);
expect(NON_QWEN_SHAPES.has('qwen-non-thinking')).toBe(false);
});
it('partition: every ShapeName is in exactly one set (no overlap, no gap)', () => {
const all: ShapeName[] = ['claude', 'qwen-thinking', 'qwen-non-thinking', 'gpt', 'generic-simple'];
for (const s of all) {
const inQwen = QWEN_TARGETED_SHAPES.has(s);
const inNonQwen = NON_QWEN_SHAPES.has(s);
expect(inQwen !== inNonQwen).toBe(true); // exactly one
}
});
});
// ───────────────────────────────────────────────────────────────────────────
// computeCostPenalty
// ───────────────────────────────────────────────────────────────────────────
describe('computeCostPenalty — brief §3.1 0.5pp per $0.10 above baseline median', () => {
it('returns 0 when candidate cost equals baseline median', () => {
expect(computeCostPenalty(0.50, 0.50)).toBe(0.0);
});
it('returns 0 when candidate cost below baseline median', () => {
expect(computeCostPenalty(0.30, 0.50)).toBe(0.0);
});
it('returns 0.005 (0.5pp) for $0.10 overage', () => {
expect(computeCostPenalty(0.60, 0.50)).toBeCloseTo(0.005, 6);
});
it('returns 0.025 (2.5pp) for $0.50 overage', () => {
expect(computeCostPenalty(1.00, 0.50)).toBeCloseTo(0.025, 6);
});
it('returns 0.05 (5pp) for $1.00 overage', () => {
expect(computeCostPenalty(1.50, 0.50)).toBeCloseTo(0.05, 6);
});
it('handles fractional overage', () => {
expect(computeCostPenalty(0.55, 0.50)).toBeCloseTo(0.0025, 6); // $0.05 overage = 0.25pp
});
});
// ───────────────────────────────────────────────────────────────────────────
// computeFitness — end-to-end aggregate
// ───────────────────────────────────────────────────────────────────────────
describe('computeFitness — Qwen-targeted shape branch', () => {
it('applies retrieval engagement bonus + cost penalty for qwen-thinking', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.50,
meanRetrievalCallsPerTask: 2.0, // expect +0.05 bonus
meanCostUsd: 0.60, // expect +0.005 cost penalty (vs 0.50 baseline)
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
expect(result.trioStrictPassRateII).toBe(0.50);
expect(result.retrievalEngagementBonus).toBe(0.05);
expect(result.costPenalty).toBeCloseTo(0.005, 6);
expect(result.fitness).toBeCloseTo(0.50 + 0.05 - 0.005, 6);
expect(result.retrievalEngagementApplied).toBe(true);
});
it('applies negative retrieval engagement bonus when below 1.5', () => {
const candidate = makeCandidate({
shape: 'qwen-non-thinking',
trioStrictPassRateII: 0.30,
meanRetrievalCallsPerTask: 1.0, // expect -0.05 bonus
meanCostUsd: 0.50, // no cost overage
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
expect(result.retrievalEngagementBonus).toBe(-0.05);
expect(result.fitness).toBeCloseTo(0.30 - 0.05 - 0.0, 6);
expect(result.retrievalEngagementApplied).toBe(true);
});
it('zero band: candidate retrieves in [1.5, 2.0) gets neutral bonus', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.40,
meanRetrievalCallsPerTask: 1.7,
meanCostUsd: 0.50,
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
expect(result.retrievalEngagementBonus).toBe(0.0);
expect(result.fitness).toBe(0.40);
});
});
describe('computeFitness — non-Qwen shape branch', () => {
it('claude shape: no retrieval engagement bonus regardless of retrieval_calls', () => {
const candidate = makeCandidate({
shape: 'claude',
trioStrictPassRateII: 0.60,
meanRetrievalCallsPerTask: 2.5, // would be +0.05 if Qwen, but excluded for claude
meanCostUsd: 0.50,
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
expect(result.retrievalEngagementBonus).toBe(0.0);
expect(result.fitness).toBe(0.60);
expect(result.retrievalEngagementApplied).toBe(false);
});
it('gpt shape: no retrieval engagement bonus regardless of retrieval_calls', () => {
const candidate = makeCandidate({
shape: 'gpt',
trioStrictPassRateII: 0.45,
meanRetrievalCallsPerTask: 0.5, // would be -0.05 if Qwen, excluded for gpt
meanCostUsd: 0.50,
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
expect(result.retrievalEngagementBonus).toBe(0.0);
expect(result.fitness).toBe(0.45);
expect(result.retrievalEngagementApplied).toBe(false);
});
it('generic-simple shape: cost penalty still applies, but no retrieval bonus', () => {
const candidate = makeCandidate({
shape: 'generic-simple',
trioStrictPassRateII: 0.70,
meanRetrievalCallsPerTask: 3.0,
meanCostUsd: 1.00, // $0.50 overage → 2.5pp penalty
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
expect(result.retrievalEngagementBonus).toBe(0.0);
expect(result.costPenalty).toBeCloseTo(0.025, 6);
expect(result.fitness).toBeCloseTo(0.70 - 0.025, 6);
});
});
describe('computeFitness — invariants', () => {
it('retrievalEngagementApplied flag matches QWEN_TARGETED_SHAPES membership', () => {
const shapes: ShapeName[] = ['claude', 'qwen-thinking', 'qwen-non-thinking', 'gpt', 'generic-simple'];
for (const shape of shapes) {
const result = computeFitness({
candidate: makeCandidate({ shape, meanRetrievalCallsPerTask: 1.5, meanCostUsd: 0.5 }),
baselineMedianCostUsd: 0.5,
});
expect(result.retrievalEngagementApplied).toBe(QWEN_TARGETED_SHAPES.has(shape));
}
});
it('fitness components sum to fitness within floating point tolerance', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.55,
meanRetrievalCallsPerTask: 2.1,
meanCostUsd: 0.65,
});
const result = computeFitness({ candidate, baselineMedianCostUsd: 0.50 });
const expected = result.trioStrictPassRateII + result.retrievalEngagementBonus - result.costPenalty;
expect(result.fitness).toBeCloseTo(expected, 10);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Amendment 7 — Tier 2 retrieval bonus (continuous)
// ───────────────────────────────────────────────────────────────────────────
describe('Amendment 7 — computeTier2RetrievalBonus (continuous formula)', () => {
it('returns 0 for non-Qwen shape regardless of retrieval delta', () => {
expect(computeTier2RetrievalBonus('claude', 2.0, 1.0)).toBe(0);
expect(computeTier2RetrievalBonus('gpt', 5.0, 1.0)).toBe(0);
expect(computeTier2RetrievalBonus('generic-simple', 3.0, 1.0)).toBe(0);
});
it('returns 0 for Qwen-targeted shape when delta ≤ 0 (no negative bonus)', () => {
expect(computeTier2RetrievalBonus('qwen-thinking', 1.0, 1.5)).toBe(0);
expect(computeTier2RetrievalBonus('qwen-thinking', 1.12, 1.12)).toBe(0); // exact zero delta
expect(computeTier2RetrievalBonus('qwen-non-thinking', 1.20, 1.25)).toBe(0); // small negative
});
it('formula: 0.05 bonus per pp above baseline (1pp = 0.01 absolute)', () => {
// baseline 1.12, candidate 1.13 = +0.01 = +1pp → 0.05 bonus
expect(computeTier2RetrievalBonus('qwen-thinking', 1.13, 1.12)).toBeCloseTo(0.05, 10);
// baseline 1.12, candidate 1.14 = +0.02 = +2pp → 0.10 bonus
expect(computeTier2RetrievalBonus('qwen-thinking', 1.14, 1.12)).toBeCloseTo(0.10, 10);
// baseline 1.12, candidate 1.15 = +0.03 = +3pp → 0.15 bonus
expect(computeTier2RetrievalBonus('qwen-thinking', 1.15, 1.12)).toBeCloseTo(0.15, 10);
// baseline 1.12, candidate 1.16 = +0.04 = +4pp → 0.20 bonus
expect(computeTier2RetrievalBonus('qwen-thinking', 1.16, 1.12)).toBeCloseTo(0.20, 10);
// baseline 1.12, candidate 1.17 = +0.05 = +5pp → 0.25 (cap)
expect(computeTier2RetrievalBonus('qwen-thinking', 1.17, 1.12)).toBeCloseTo(0.25, 10);
});
it('cap holds at +5pp absolute and beyond (cap = 0.25)', () => {
expect(computeTier2RetrievalBonus('qwen-thinking', 1.20, 1.12)).toBe(TIER_2_BONUS_CAP);
expect(computeTier2RetrievalBonus('qwen-thinking', 2.00, 1.12)).toBe(TIER_2_BONUS_CAP);
expect(computeTier2RetrievalBonus('qwen-thinking', 5.00, 1.12)).toBe(TIER_2_BONUS_CAP);
expect(computeTier2RetrievalBonus('qwen-non-thinking', 1.30, 1.25)).toBe(TIER_2_BONUS_CAP);
});
it('Tier 2 weight constants match Amendment 7 §fitness_function_tiered.tier_2', () => {
expect(TIER_2_BONUS_PER_PP).toBe(0.05);
expect(TIER_2_BONUS_CAP).toBe(0.25);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Amendment 7 — computeTieredFitness (Tier 1/2/3 + saturated regime aggregate)
// ───────────────────────────────────────────────────────────────────────────
describe('Amendment 7 — computeTieredFitness', () => {
it('Tier 1 = NULL pass rate delta in pp (signed)', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.95,
meanRetrievalCallsPerTask: 1.12,
});
const result = computeTieredFitness({
candidate,
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
mutationValidatorPassed: true,
saturatedRegime: true,
});
expect(result.tier1DeltaPP).toBeCloseTo(7.5, 10); // 0.95 - 0.875 = 0.075 = 7.5pp
});
it('Tier 1 negative when candidate regresses below NULL baseline', () => {
const candidate = makeCandidate({
shape: 'claude',
trioStrictPassRateII: 0.75,
meanRetrievalCallsPerTask: 1.12,
});
const result = computeTieredFitness({
candidate,
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
mutationValidatorPassed: true,
saturatedRegime: true,
});
expect(result.tier1DeltaPP).toBeCloseTo(-12.5, 10); // 0.75 - 0.875 = -0.125 = -12.5pp
});
it('Tier 2 only applies to Qwen-targeted shapes', () => {
for (const shape of ['claude', 'gpt', 'generic-simple'] as const) {
const result = computeTieredFitness({
candidate: makeCandidate({ shape, meanRetrievalCallsPerTask: 5.0 }),
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
mutationValidatorPassed: true,
saturatedRegime: true,
});
expect(result.tier2RetrievalBonus).toBe(0);
}
});
it('Tier 3 = 0.10 if mutation validator passed; 0 otherwise', () => {
const base = {
candidate: makeCandidate({ shape: 'claude' }),
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
saturatedRegime: true,
};
expect(computeTieredFitness({ ...base, mutationValidatorPassed: true }).tier3CellSemanticInvarianceBonus).toBe(
TIER_3_BONUS_FULL_INVARIANCE,
);
expect(computeTieredFitness({ ...base, mutationValidatorPassed: false }).tier3CellSemanticInvarianceBonus).toBe(0);
});
it('cellSemanticAnchorInvarianceCount: 7 if validator passed; 0 otherwise', () => {
const base = {
candidate: makeCandidate({ shape: 'claude' }),
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
saturatedRegime: true,
};
expect(computeTieredFitness({ ...base, mutationValidatorPassed: true }).cellSemanticAnchorInvarianceCount).toBe(
TIER_3_ANCHOR_COUNT_FULL,
);
expect(computeTieredFitness({ ...base, mutationValidatorPassed: false }).cellSemanticAnchorInvarianceCount).toBe(0);
});
it('aggregateSaturatedRegime = tier_2 + tier_3 (Tier 1 NOT included)', () => {
const candidate = makeCandidate({
shape: 'qwen-thinking',
trioStrictPassRateII: 0.95, // would give tier1 = 7.5pp
meanRetrievalCallsPerTask: 1.15, // 1.12 baseline → +3pp → tier2 = 0.15
});
const result = computeTieredFitness({
candidate,
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
mutationValidatorPassed: true, // tier3 = 0.10
saturatedRegime: true,
});
expect(result.aggregateSaturatedRegime).toBeCloseTo(0.15 + 0.10, 10); // 0.25
expect(result.aggregateSaturatedRegime).not.toBeCloseTo(7.5 + 0.15 + 0.10, 1); // tier1 not in aggregate
});
it('saturatedRegimeApplied flag mirrors input', () => {
const base = {
candidate: makeCandidate({ shape: 'claude' }),
nullBaselinePassRateII: 0.875,
nullBaselineMeanRetrievalCallsPerTask: 1.12,
mutationValidatorPassed: true,
};
expect(computeTieredFitness({ ...base, saturatedRegime: true }).saturatedRegimeApplied).toBe(true);
expect(computeTieredFitness({ ...base, saturatedRegime: false }).saturatedRegimeApplied).toBe(false);
});
it('NULL_BASELINE_PER_SHAPE constants match Checkpoint A v2 §B.2 pinned values', () => {
expect(NULL_BASELINE_PER_SHAPE.claude.trioStrictPassRateII).toBe(0.875);
expect(NULL_BASELINE_PER_SHAPE['qwen-thinking'].trioStrictPassRateII).toBe(0.875);
expect(NULL_BASELINE_PER_SHAPE['qwen-non-thinking'].trioStrictPassRateII).toBe(1.0);
expect(NULL_BASELINE_PER_SHAPE.gpt.trioStrictPassRateII).toBe(0.75);
expect(NULL_BASELINE_PER_SHAPE['generic-simple'].trioStrictPassRateII).toBe(0.875);
expect(NULL_BASELINE_AGGREGATE.trioStrictPassRateII).toBe(0.875);
expect(NULL_BASELINE_AGGREGATE.meanRetrievalCallsPerTask).toBe(1.12);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Amendment 7 — computeDeltaFloorVerdict (3 OR-gated thresholds)
// ───────────────────────────────────────────────────────────────────────────
describe('Amendment 7 — computeDeltaFloorVerdict (§gen_1_pre_registered_delta_floor)', () => {
it('PROCEED if threshold 1 (aggregate Tier 1 ≥+3pp) passes alone', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.910, // +3.5pp vs 0.875 NULL
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: { 'qwen-thinking': 1.12 }, // no Qwen retrieval signal
qwenShapeNullBaselineRetrievalMeans: { 'qwen-thinking': 1.12 },
qwenAggregateTier2Bonus: 0,
});
expect(verdict.threshold1AggregateTier1).toBe('PASS');
expect(verdict.threshold1ValuePP).toBeCloseTo(3.5, 10);
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('FAIL');
expect(verdict.threshold3CompoundTier1PlusTier2).toBe('FAIL');
expect(verdict.overallVerdict).toBe('PROCEED');
});
it('PROCEED if threshold 2 (Qwen retrieval ≥+0.10) passes alone', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.875, // exactly NULL → 0pp (fails threshold 1 ≥+3pp)
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: { 'qwen-thinking': 1.30 }, // 1.30 - 1.12 = +0.18 ≥ 0.10
qwenShapeNullBaselineRetrievalMeans: { 'qwen-thinking': 1.12 },
qwenAggregateTier2Bonus: 0, // not enough for threshold 3
});
expect(verdict.threshold1AggregateTier1).toBe('FAIL');
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('PASS');
expect(verdict.threshold2MaxDeltaAbsolute).toBeCloseTo(0.18, 10);
expect(verdict.overallVerdict).toBe('PROCEED');
});
it('PROCEED if threshold 3 (Tier 1 ≥0pp AND Tier 2 ≥0.05) passes alone', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.880, // +0.5pp ≥ 0pp; fails threshold 1 ≥+3pp
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: { 'qwen-thinking': 1.13 }, // +0.01 < 0.10 — fails threshold 2
qwenShapeNullBaselineRetrievalMeans: { 'qwen-thinking': 1.12 },
qwenAggregateTier2Bonus: 0.05, // ≥ 0.05
});
expect(verdict.threshold1AggregateTier1).toBe('FAIL');
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('FAIL');
expect(verdict.threshold3CompoundTier1PlusTier2).toBe('PASS');
expect(verdict.overallVerdict).toBe('PROCEED');
});
it('HALT_INVESTIGATE if all three thresholds fail', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.870, // -0.5pp — fails threshold 1 + threshold 3 (Tier 1 < 0pp)
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: { 'qwen-thinking': 1.13 }, // +0.01 < 0.10
qwenShapeNullBaselineRetrievalMeans: { 'qwen-thinking': 1.12 },
qwenAggregateTier2Bonus: 0.04, // < 0.05
});
expect(verdict.threshold1AggregateTier1).toBe('FAIL');
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('FAIL');
expect(verdict.threshold3CompoundTier1PlusTier2).toBe('FAIL');
expect(verdict.overallVerdict).toBe('HALT_INVESTIGATE');
});
it('threshold 1 exact-boundary: 3.0pp passes (≥+3pp inclusive with EPSILON)', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.905, // +3.0pp exactly
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: {},
qwenShapeNullBaselineRetrievalMeans: {},
qwenAggregateTier2Bonus: 0,
});
expect(verdict.threshold1AggregateTier1).toBe('PASS');
expect(verdict.threshold1ValuePP).toBeCloseTo(3.0, 10);
});
it('threshold 2 exact-boundary: +0.10 absolute passes (≥+0.10 inclusive with EPSILON)', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.875,
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: { 'qwen-thinking': 1.22 }, // 1.22 - 1.12 = +0.10 exact
qwenShapeNullBaselineRetrievalMeans: { 'qwen-thinking': 1.12 },
qwenAggregateTier2Bonus: 0,
});
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('PASS');
expect(verdict.threshold2MaxDeltaAbsolute).toBeCloseTo(0.10, 10);
});
it('threshold 2 takes max delta across multiple Qwen shapes', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.875,
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: {
'qwen-thinking': 1.13, // +0.01
'qwen-non-thinking': 1.40, // +0.15
},
qwenShapeNullBaselineRetrievalMeans: {
'qwen-thinking': 1.12,
'qwen-non-thinking': 1.25,
},
qwenAggregateTier2Bonus: 0,
});
expect(verdict.threshold2MaxDeltaAbsolute).toBeCloseTo(0.15, 10);
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('PASS');
});
it('handles empty Qwen data: threshold 2 max delta = 0 (FAIL since 0 < 0.10)', () => {
const verdict = computeDeltaFloorVerdict({
aggregateTrioStrictPassRateII: 0.875,
aggregateNullBaselinePassRateII: 0.875,
qwenShapeRetrievalMeans: {},
qwenShapeNullBaselineRetrievalMeans: {},
qwenAggregateTier2Bonus: 0,
});
expect(verdict.threshold2MaxDeltaAbsolute).toBe(0);
expect(verdict.threshold2QwenRetrievalAbsolute).toBe('FAIL');
});
it('Δ-floor threshold constants match Amendment 7 §gen_1_pre_registered_delta_floor', () => {
expect(DELTA_FLOOR_THRESHOLDS.threshold1AggregateTier1PP).toBe(3);
expect(DELTA_FLOOR_THRESHOLDS.threshold2QwenRetrievalAbsolute).toBe(0.10);
expect(DELTA_FLOOR_THRESHOLDS.threshold3Tier1MinPP).toBe(0);
expect(DELTA_FLOOR_THRESHOLDS.threshold3Tier2MinBonus).toBe(0.05);
});
});

View File

@@ -0,0 +1,204 @@
/**
* GEPA Faza 1 — κ audit utility tests.
*
* Coverage targets:
* - Drift band semantics: PASS / DRIFT_LOW / DRIFT_HIGH per Faza 1 §F.3
* - Conservative trio = min of three pairwise (matches manifest v6 §5.4 + v7 anchor)
* - v6 policy floor cross-validation reporting
* - Cohen's κ computation against known-correct values from kappa-recal artifact
*/
import { describe, expect, it } from 'vitest';
import {
CANONICAL_KAPPA,
KAPPA_DRIFT_THRESHOLD,
KAPPA_DRIFT_BAND_LOW,
KAPPA_DRIFT_BAND_HIGH,
V6_KAPPA_POLICY_FLOOR_PASS,
auditKappa,
computeCohensKappa,
} from '../../src/faza-1/kappa-audit.js';
// ───────────────────────────────────────────────────────────────────────────
// Constants exposed for external auditing
// ───────────────────────────────────────────────────────────────────────────
describe('canonical κ + drift band constants', () => {
it('CANONICAL_KAPPA matches kappa-recal artifact value (0.7877758913412564)', () => {
expect(CANONICAL_KAPPA).toBe(0.7877758913412564);
});
it('drift threshold is 0.05 per brief §4 condition 3', () => {
expect(KAPPA_DRIFT_THRESHOLD).toBe(0.05);
});
it('drift band low = canonical - 0.05', () => {
// Canonical is 0.7877758913412564; minus 0.05 = 0.7377758913412564 ≈ 0.7378.
// toBeCloseTo precision 4 = absolute diff < 5e-5 (covers the ~2.4e-5 rounding gap).
expect(KAPPA_DRIFT_BAND_LOW).toBeCloseTo(0.7378, 4);
// Stronger invariant: equals canonical minus drift threshold exactly (within IEEE 754).
expect(KAPPA_DRIFT_BAND_LOW).toBe(CANONICAL_KAPPA - KAPPA_DRIFT_THRESHOLD);
});
it('drift band high = canonical + 0.05', () => {
expect(KAPPA_DRIFT_BAND_HIGH).toBeCloseTo(0.8378, 4);
expect(KAPPA_DRIFT_BAND_HIGH).toBe(CANONICAL_KAPPA + KAPPA_DRIFT_THRESHOLD);
});
it('v6 policy floor pass threshold is 0.70', () => {
expect(V6_KAPPA_POLICY_FLOOR_PASS).toBe(0.70);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Drift band verdicts
// ───────────────────────────────────────────────────────────────────────────
describe('auditKappa — drift band verdicts', () => {
it('canonical value triggers PASS_WITHIN_DRIFT_BAND', () => {
const r = auditKappa({ kOpusGpt: CANONICAL_KAPPA, kOpusMinimax: CANONICAL_KAPPA, kGptMinimax: CANONICAL_KAPPA });
expect(r.kConservativeTrio).toBe(CANONICAL_KAPPA);
expect(r.verdict).toBe('PASS_WITHIN_DRIFT_BAND');
expect(r.driftFromCanonical).toBe(0);
});
it('value below band low triggers DRIFT_LOW_BELOW_BAND', () => {
const r = auditKappa({ kOpusGpt: 0.85, kOpusMinimax: 0.85, kGptMinimax: 0.70 });
// min = 0.70 < 0.7378 band low
expect(r.verdict).toBe('DRIFT_LOW_BELOW_BAND');
});
it('value above band high triggers DRIFT_HIGH_ABOVE_BAND', () => {
const r = auditKappa({ kOpusGpt: 0.90, kOpusMinimax: 0.90, kGptMinimax: 0.85 });
// min = 0.85 > 0.8378 band high
expect(r.verdict).toBe('DRIFT_HIGH_ABOVE_BAND');
});
it('exact band low boundary inclusive (PASS)', () => {
const r = auditKappa({ kOpusGpt: 1.0, kOpusMinimax: 1.0, kGptMinimax: KAPPA_DRIFT_BAND_LOW });
expect(r.verdict).toBe('PASS_WITHIN_DRIFT_BAND');
});
it('exact band high boundary inclusive (PASS)', () => {
const r = auditKappa({ kOpusGpt: KAPPA_DRIFT_BAND_HIGH, kOpusMinimax: KAPPA_DRIFT_BAND_HIGH, kGptMinimax: KAPPA_DRIFT_BAND_HIGH });
expect(r.verdict).toBe('PASS_WITHIN_DRIFT_BAND');
});
});
// ───────────────────────────────────────────────────────────────────────────
// Conservative trio = min — anchor against actual kappa-recal data
// ───────────────────────────────────────────────────────────────────────────
describe('auditKappa — conservative trio = min of pairwise', () => {
it('reproduces v6-kappa-recal conservative trio from pairwise', () => {
// From benchmarks/calibration/v6-kappa-recal/_summary-v6-kappa.json
// (SHA 657d4490... pinned in launch decision §B)
const r = auditKappa({
kOpusGpt: 0.847958297132928,
kOpusMinimax: 0.8548922056384745,
kGptMinimax: 0.7877758913412564,
});
expect(r.kConservativeTrio).toBe(0.7877758913412564);
expect(r.verdict).toBe('PASS_WITHIN_DRIFT_BAND');
expect(r.v6PolicyFloorPass).toBe(true);
});
it('v6 policy floor PASS when conservative >= 0.70', () => {
const r = auditKappa({ kOpusGpt: 0.75, kOpusMinimax: 0.72, kGptMinimax: 0.70 });
expect(r.v6PolicyFloorPass).toBe(true);
});
it('v6 policy floor FAIL when conservative < 0.70', () => {
const r = auditKappa({ kOpusGpt: 0.75, kOpusMinimax: 0.72, kGptMinimax: 0.65 });
expect(r.v6PolicyFloorPass).toBe(false);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Audit log line format
// ───────────────────────────────────────────────────────────────────────────
describe('auditKappa — audit log line format', () => {
it('audit log contains all required fields', () => {
const r = auditKappa({ kOpusGpt: 0.85, kOpusMinimax: 0.84, kGptMinimax: 0.78 });
expect(r.auditLogLine).toContain('κ_conservative_trio=');
expect(r.auditLogLine).toContain('canonical=');
expect(r.auditLogLine).toContain('drift=');
expect(r.auditLogLine).toContain('verdict=');
expect(r.auditLogLine).toContain('v6_policy_floor=');
});
it('positive drift includes + sign', () => {
const r = auditKappa({ kOpusGpt: 0.83, kOpusMinimax: 0.83, kGptMinimax: 0.80 });
expect(r.driftFromCanonical).toBeGreaterThan(0);
expect(r.auditLogLine).toContain('drift=+');
});
it('negative drift uses - sign', () => {
const r = auditKappa({ kOpusGpt: 0.85, kOpusMinimax: 0.85, kGptMinimax: 0.70 });
expect(r.driftFromCanonical).toBeLessThan(0);
expect(r.auditLogLine).toContain('drift=-');
});
});
// ───────────────────────────────────────────────────────────────────────────
// Cohen's κ primitive
// ───────────────────────────────────────────────────────────────────────────
describe('computeCohensKappa primitive', () => {
it('returns 1.0 for perfect agreement', () => {
const k = computeCohensKappa({
bothCorrect: 50,
bothIncorrect: 50,
firstCorrectSecondIncorrect: 0,
firstIncorrectSecondCorrect: 0,
});
expect(k).toBe(1.0);
});
it('returns 0 for chance-level agreement (50/50 base rate, random co-occurrence)', () => {
// 100 trials, both raters each correct 50% with independent assignment
const k = computeCohensKappa({
bothCorrect: 25,
bothIncorrect: 25,
firstCorrectSecondIncorrect: 25,
firstIncorrectSecondCorrect: 25,
});
expect(k).toBeCloseTo(0, 2);
});
it('reproduces v6 Opus-vs-GPT pairwise κ from kappa-recal data', () => {
// From _summary-v6-kappa.json line 58-63 confusion_opus_gpt:
// correct_correct: 32, incorrect_incorrect: 61
// correct_incorrect: 7, incorrect_correct: 0
// expected κ = 0.847958297132928 (per same file line 3 k_opus_gpt)
const k = computeCohensKappa({
bothCorrect: 32,
bothIncorrect: 61,
firstCorrectSecondIncorrect: 7,
firstIncorrectSecondCorrect: 0,
});
expect(k).toBeCloseTo(0.847958297132928, 6);
});
it('returns NaN for empty observation set', () => {
const k = computeCohensKappa({
bothCorrect: 0,
bothIncorrect: 0,
firstCorrectSecondIncorrect: 0,
firstIncorrectSecondCorrect: 0,
});
expect(Number.isNaN(k)).toBe(true);
});
it('handles unanimous-correct edge case (100% base rate, no variance)', () => {
// Both raters agree everything is correct — observed = 1, expected = 1, κ undefined → return 1
const k = computeCohensKappa({
bothCorrect: 100,
bothIncorrect: 0,
firstCorrectSecondIncorrect: 0,
firstIncorrectSecondCorrect: 0,
});
expect(k).toBe(1.0);
});
});

View File

@@ -0,0 +1,180 @@
/**
* GEPA Faza 1 — mutation oracle fork tests.
*
* Coverage targets per manifest v7 §amendment_2_integration.scaffold_test_coverage_NEW_requirements
* mandatory_routing_tests:
* - Qwen branch: qwen-thinking + qwen-non-thinking → "qwen" template
* - Non-Qwen branch: claude + gpt + generic-simple → "non-qwen" template
* - Template content actually exists at expected paths
* - Placeholder substitution works for both branches
*/
import { describe, expect, it } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import {
classifyShape,
templatePathForShape,
loadTemplate,
buildOraclePrompt,
} from '../../src/faza-1/mutation-oracle-fork.js';
import { type ShapeName } from '../../src/faza-1/types.js';
const ORACLE_DIR = path.resolve(__dirname, '../../oracle/faza-1');
// ───────────────────────────────────────────────────────────────────────────
// Shape classification — Amendment 2 §4 mandatory routing tests
// ───────────────────────────────────────────────────────────────────────────
describe('classifyShape — Amendment 2 §4 fork routing', () => {
it('qwen-thinking → qwen branch', () => {
expect(classifyShape('qwen-thinking')).toBe('qwen');
});
it('qwen-non-thinking → qwen branch', () => {
expect(classifyShape('qwen-non-thinking')).toBe('qwen');
});
it('claude → non-qwen branch', () => {
expect(classifyShape('claude')).toBe('non-qwen');
});
it('gpt → non-qwen branch', () => {
expect(classifyShape('gpt')).toBe('non-qwen');
});
it('generic-simple → non-qwen branch', () => {
expect(classifyShape('generic-simple')).toBe('non-qwen');
});
it('partition: every ShapeName routes to exactly one branch', () => {
const all: ShapeName[] = ['claude', 'qwen-thinking', 'qwen-non-thinking', 'gpt', 'generic-simple'];
const qwenCount = all.filter(s => classifyShape(s) === 'qwen').length;
const nonQwenCount = all.filter(s => classifyShape(s) === 'non-qwen').length;
expect(qwenCount).toBe(2);
expect(nonQwenCount).toBe(3);
expect(qwenCount + nonQwenCount).toBe(all.length);
});
});
// ───────────────────────────────────────────────────────────────────────────
// Template paths
// ───────────────────────────────────────────────────────────────────────────
describe('templatePathForShape', () => {
it('Qwen-targeted shapes resolve to mutation-prompt-template-qwen.md', () => {
expect(path.basename(templatePathForShape('qwen-thinking'))).toBe('mutation-prompt-template-qwen.md');
expect(path.basename(templatePathForShape('qwen-non-thinking'))).toBe('mutation-prompt-template-qwen.md');
});
it('Non-Qwen shapes resolve to mutation-prompt-template-non-qwen.md', () => {
expect(path.basename(templatePathForShape('claude'))).toBe('mutation-prompt-template-non-qwen.md');
expect(path.basename(templatePathForShape('gpt'))).toBe('mutation-prompt-template-non-qwen.md');
expect(path.basename(templatePathForShape('generic-simple'))).toBe('mutation-prompt-template-non-qwen.md');
});
it('Both template files exist on disk in the oracle directory', () => {
expect(fs.existsSync(path.join(ORACLE_DIR, 'mutation-prompt-template-qwen.md'))).toBe(true);
expect(fs.existsSync(path.join(ORACLE_DIR, 'mutation-prompt-template-non-qwen.md'))).toBe(true);
});
});
// ───────────────────────────────────────────────────────────────────────────
// loadTemplate
// ───────────────────────────────────────────────────────────────────────────
describe('loadTemplate', () => {
it('Qwen template contains anti-premature-finalization scaffolding', () => {
const t = loadTemplate('qwen-thinking');
// Case-insensitive: template prose uses lowercase, headings may capitalize.
expect(t.toLowerCase()).toContain('anti-premature-finalization');
// These instruction phrases are unique to the Qwen branch:
expect(t).toContain('Continue retrieving until');
expect(t).toContain('multi-turn retrieval');
});
it('Non-Qwen template lacks Qwen-specific scaffolding instructions', () => {
const t = loadTemplate('claude');
expect(t).toContain('Standard mutation directions');
expect(t).toContain('reasoning scaffold');
// The non-qwen template legitimately MENTIONS the absence of qwen
// scaffolding ("no Qwen-specific anti-premature-finalization scaffolding
// required"). The proper test is for absence of the actual prescriptive
// INSTRUCTION phrases that appear only in the Qwen template.
expect(t).not.toContain('Continue retrieving until');
expect(t).not.toContain('multi-turn retrieval');
});
it('Both templates lock cell semantic boundaries', () => {
const qwenT = loadTemplate('qwen-thinking');
const nonQwenT = loadTemplate('claude');
for (const t of [qwenT, nonQwenT]) {
expect(t).toContain('DO NOT modify');
expect(t).toContain('MULTI_STEP_ACTION_CONTRACT');
}
});
it('Both templates expose all 4 placeholder tokens', () => {
const placeholders = [
'###BASELINE_SHAPE_CONTENT###',
'###FAILURE_MODE_SUMMARY###',
'###SHAPE_NAME###',
'###TEMPLATE_CLASS###',
];
for (const shape of ['qwen-thinking', 'claude'] as ShapeName[]) {
const t = loadTemplate(shape);
for (const ph of placeholders) {
expect(t).toContain(ph);
}
}
});
});
// ───────────────────────────────────────────────────────────────────────────
// buildOraclePrompt — placeholder substitution
// ───────────────────────────────────────────────────────────────────────────
describe('buildOraclePrompt', () => {
it('substitutes all 4 placeholders in Qwen template', () => {
const prompt = buildOraclePrompt({
shape: 'qwen-thinking',
baselineShapeContent: 'export const baselineShape = {...};',
failureModeSummary: '1. unsupported-specifics\n2. missed\n3. shallow',
});
expect(prompt).toContain('export const baselineShape');
expect(prompt).toContain('unsupported-specifics');
expect(prompt).toContain('qwen-thinking');
expect(prompt).toContain('Template class:** qwen');
expect(prompt).not.toContain('###BASELINE_SHAPE_CONTENT###');
expect(prompt).not.toContain('###FAILURE_MODE_SUMMARY###');
expect(prompt).not.toContain('###SHAPE_NAME###');
expect(prompt).not.toContain('###TEMPLATE_CLASS###');
});
it('substitutes all 4 placeholders in non-Qwen template', () => {
const prompt = buildOraclePrompt({
shape: 'claude',
baselineShapeContent: 'export const claudeShape = {...};',
failureModeSummary: '1. conflation\n2. weak-synthesis\n3. fabrication',
});
expect(prompt).toContain('export const claudeShape');
expect(prompt).toContain('conflation');
expect(prompt).toContain('claude');
expect(prompt).toContain('Template class:** non-qwen');
});
it('Qwen-targeted shapes get the same template (qwen-thinking + qwen-non-thinking interchangeable)', () => {
const a = buildOraclePrompt({
shape: 'qwen-thinking',
baselineShapeContent: 'X',
failureModeSummary: 'Y',
});
const b = buildOraclePrompt({
shape: 'qwen-non-thinking',
baselineShapeContent: 'X',
failureModeSummary: 'Y',
});
// Same template body, but ###SHAPE_NAME### is substituted differently
expect(a.replace(/qwen-thinking/g, 'X')).toBe(b.replace(/qwen-non-thinking/g, 'X'));
});
});

View File

@@ -0,0 +1,48 @@
/**
* GEPA Faza 1 — regression test for null-baseline promptShapeOverride bug.
*
* Per Amendment 6: the original NULL-baseline runner passed `shape: PromptShape`
* to runOneEval but never forwarded it to runRetrievalAgentLoop, meaning all
* 40 evals used the model-alias-default shape (qwen-thinking for Qwen subject)
* regardless of the per-shape evaluation label.
*
* Fix: pass `promptShapeOverride: shape.name` to runRetrievalAgentLoop.
*
* This test verifies the fix is present in the script source — a structural
* source-text invariant. A behavioral test would require refactoring the runner
* to expose a testable function; for the Faza 1 timeline, source-text check is
* sufficient regression protection.
*/
import { describe, expect, it } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
const RUNNER_PATH = path.resolve(__dirname, '../../scripts/faza-1/run-null-baseline.ts');
describe('Amendment 6 regression — null-baseline promptShapeOverride wiring', () => {
const source = fs.readFileSync(RUNNER_PATH, 'utf-8');
it('script source contains promptShapeOverride passed to runRetrievalAgentLoop', () => {
// The fix introduces the literal `promptShapeOverride: shape.name` in the
// runRetrievalAgentLoop call within runOneEval.
expect(source).toContain('promptShapeOverride: shape.name');
});
it('script source contains Amendment 6 bug-fix annotation comment', () => {
expect(source).toContain('Amendment 6');
expect(source).toContain('bug fix per Amendment 6');
});
it('script source still passes modelAlias = SUBJECT_ALIAS', () => {
// Make sure the fix didn't inadvertently change the subject (which is
// shape-independent: subject is always Qwen, override controls shape).
expect(source).toContain('modelAlias: SUBJECT_ALIAS');
});
it('runOneEval receives shape parameter typed as PromptShape', () => {
// The shape parameter must remain in scope so promptShapeOverride: shape.name
// resolves correctly.
expect(source).toMatch(/runOneEval\s*\(\s*shape\s*:\s*PromptShape/);
});
});

View File

@@ -0,0 +1,206 @@
/**
* GEPA Faza 1 — selection tests.
*
* Coverage targets:
* - top-1-per-shape selection by fitness
* - acceptance verdict per best-per-shape
* - run-aggregate §F.2 condition (≥3/5 shapes positive delta)
* - error handling: missing baseline entry
*/
import { describe, expect, it } from 'vitest';
import { runSelection } from '../../src/faza-1/selection.js';
import { type CandidateMetrics, type ShapeName } from '../../src/faza-1/types.js';
function makeCandidate(
shape: ShapeName,
candidateId: string,
trioII: number,
retrieval: number = 1.5,
cost: number = 0.5,
): CandidateMetrics {
return {
candidateId,
shape,
evaluations: [],
trioStrictPassRateII: trioII,
trioStrictPassRateI: trioII, // simplified for test
meanRetrievalCallsPerTask: retrieval,
meanCostUsd: cost,
};
}
describe('runSelection — top-1 per shape', () => {
it('selects highest-fitness candidate per shape', () => {
const candidatesPerShape = new Map<ShapeName, CandidateMetrics[]>([
['claude', [
makeCandidate('claude', 'c-low', 0.30),
makeCandidate('claude', 'c-high', 0.50),
makeCandidate('claude', 'c-mid', 0.40),
]],
]);
const baselineRate = new Map<ShapeName, number>([['claude', 0.20]]);
const baselineCost = new Map<ShapeName, number>([['claude', 0.50]]);
const report = runSelection({
candidatesPerShape,
baselineTrioStrictPassRateII: baselineRate,
baselineMedianCostUsd: baselineCost,
});
expect(report.perShape).toHaveLength(1);
expect(report.perShape[0].shape).toBe('claude');
expect(report.perShape[0].bestCandidate.candidateId).toBe('c-high');
expect(report.perShape[0].allCandidatesRanked).toHaveLength(3);
// Sorted descending by fitness
expect(report.perShape[0].allCandidatesRanked[0].candidate.candidateId).toBe('c-high');
expect(report.perShape[0].allCandidatesRanked[2].candidate.candidateId).toBe('c-low');
});
it('skips shapes with empty candidate lists', () => {
const candidatesPerShape = new Map<ShapeName, CandidateMetrics[]>([
['claude', []],
['gpt', [makeCandidate('gpt', 'g1', 0.40)]],
]);
const baselineRate = new Map<ShapeName, number>([
['claude', 0.20],
['gpt', 0.20],
]);
const baselineCost = new Map<ShapeName, number>([
['claude', 0.50],
['gpt', 0.50],
]);
const report = runSelection({
candidatesPerShape,
baselineTrioStrictPassRateII: baselineRate,
baselineMedianCostUsd: baselineCost,
});
expect(report.perShape).toHaveLength(1);
expect(report.perShape[0].shape).toBe('gpt');
});
it('throws on missing baseline trio_strict rate for a shape', () => {
const candidatesPerShape = new Map<ShapeName, CandidateMetrics[]>([
['claude', [makeCandidate('claude', 'c1', 0.30)]],
]);
expect(() =>
runSelection({
candidatesPerShape,
baselineTrioStrictPassRateII: new Map(),
baselineMedianCostUsd: new Map([['claude', 0.50]]),
}),
).toThrow(/missing baseline trio_strict/);
});
it('throws on missing baseline median cost for a shape', () => {
const candidatesPerShape = new Map<ShapeName, CandidateMetrics[]>([
['claude', [makeCandidate('claude', 'c1', 0.30)]],
]);
expect(() =>
runSelection({
candidatesPerShape,
baselineTrioStrictPassRateII: new Map([['claude', 0.20]]),
baselineMedianCostUsd: new Map(),
}),
).toThrow(/missing baseline median cost/);
});
});
describe('runSelection — Qwen retrieval engagement bonus affects ranking', () => {
it('Qwen candidate with higher retrieval engagement outranks higher trio_strict but low retrieval', () => {
// Candidate A: trio=0.40, retrieval=1.0 (-0.05 bonus → fitness ~0.35)
// Candidate B: trio=0.36, retrieval=2.0 (+0.05 bonus → fitness ~0.41)
// B wins despite lower trio_strict, because the bonus tips it
const candidatesPerShape = new Map<ShapeName, CandidateMetrics[]>([
['qwen-thinking', [
makeCandidate('qwen-thinking', 'A-high-trio-low-retrieval', 0.40, 1.0),
makeCandidate('qwen-thinking', 'B-mid-trio-high-retrieval', 0.36, 2.0),
]],
]);
const report = runSelection({
candidatesPerShape,
baselineTrioStrictPassRateII: new Map([['qwen-thinking', 0.20]]),
baselineMedianCostUsd: new Map([['qwen-thinking', 0.50]]),
});
expect(report.perShape[0].bestCandidate.candidateId).toBe('B-mid-trio-high-retrieval');
});
it('Non-Qwen ranking depends on trio_strict alone (no retrieval bonus tip)', () => {
// Same trio_strict + retrieval setup as above but for claude shape
// Now A wins (higher trio_strict) because no retrieval bonus applies
const candidatesPerShape = new Map<ShapeName, CandidateMetrics[]>([
['claude', [
makeCandidate('claude', 'A-high-trio', 0.40, 1.0),
makeCandidate('claude', 'B-mid-trio', 0.36, 2.0),
]],
]);
const report = runSelection({
candidatesPerShape,
baselineTrioStrictPassRateII: new Map([['claude', 0.20]]),
baselineMedianCostUsd: new Map([['claude', 0.50]]),
});
expect(report.perShape[0].bestCandidate.candidateId).toBe('A-high-trio');
});
});
describe('runSelection — run-aggregate §F.2 condition (≥3/5 shapes positive delta)', () => {
function setupAllShapes(deltas: Record<ShapeName, number>) {
const candidates = new Map<ShapeName, CandidateMetrics[]>();
const baselineRates = new Map<ShapeName, number>();
const baselineCosts = new Map<ShapeName, number>();
const BASELINE = 0.20;
for (const [shape, delta] of Object.entries(deltas) as Array<[ShapeName, number]>) {
candidates.set(shape, [
makeCandidate(shape, `${shape}-best`, BASELINE + delta / 100, 2.0),
]);
baselineRates.set(shape, BASELINE);
baselineCosts.set(shape, 0.5);
}
return { candidates, baselineRates, baselineCosts };
}
it('PASS §F.2: 5/5 shapes positive', () => {
const { candidates, baselineRates, baselineCosts } = setupAllShapes({
'claude': 6, 'qwen-thinking': 6, 'qwen-non-thinking': 6, 'gpt': 6, 'generic-simple': 6,
});
const report = runSelection({
candidatesPerShape: candidates,
baselineTrioStrictPassRateII: baselineRates,
baselineMedianCostUsd: baselineCosts,
});
expect(report.runAggregate.shapesWithPositiveDelta).toBe(5);
expect(report.runAggregate.condition2Pass).toBe(true);
});
it('PASS §F.2: 3/5 shapes positive (boundary)', () => {
const { candidates, baselineRates, baselineCosts } = setupAllShapes({
'claude': 6, 'qwen-thinking': 6, 'qwen-non-thinking': 6, 'gpt': -2, 'generic-simple': -2,
});
const report = runSelection({
candidatesPerShape: candidates,
baselineTrioStrictPassRateII: baselineRates,
baselineMedianCostUsd: baselineCosts,
});
expect(report.runAggregate.shapesWithPositiveDelta).toBe(3);
expect(report.runAggregate.condition2Pass).toBe(true);
});
it('FAIL §F.2: 2/5 shapes positive', () => {
const { candidates, baselineRates, baselineCosts } = setupAllShapes({
'claude': 6, 'qwen-thinking': 6, 'qwen-non-thinking': -2, 'gpt': -2, 'generic-simple': -2,
});
const report = runSelection({
candidatesPerShape: candidates,
baselineTrioStrictPassRateII: baselineRates,
baselineMedianCostUsd: baselineCosts,
});
expect(report.runAggregate.shapesWithPositiveDelta).toBe(2);
expect(report.runAggregate.condition2Pass).toBe(false);
});
});