mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
df183d1ea4
Addresses the CRITICAL/HIGH/MED findings from the brutal-honesty review of the learning-integrity cluster. The theme: the machinery was real but several claims outran the evidence. This makes the claims honest and the evidence falsifiable. CRIT-1 / HIGH-3 (flywheel gated vapor / toy anchor can't move): - verification/anchors/qe-anchor-v2.json — 5 oracle-verified DISCRIMINATING items (13 mutants), each with a trap edge a happy-path test misses, so the anchor CAN actually move. v1's items were too easy to discriminate a retrieval effect. - scripts/coupling-experiment.ts — falsifiable NONE/IRRELEVANT/RELEVANT controlled test (N reps, temp>0, $0 mutation-oracle grading). Live run remains blocked on model availability; the harness refuses to fabricate a verdict. CRIT-2 (DoE "stats" were a range proxy): - scripts/doe-run.ts — N real replicates of a FIXED item at temp=0.6 (was 5 different items x1 at temp=0), per-replicate seed, plus a deterministic $0 --stub. - scripts/doe-aggregate.mjs — real main-effects factorial ANOVA: within-cell error term, F-test via dependency-free regularized incomplete beta; beads verdict now gates on p<0.05, not an eyeballed mean delta. - tests/unit/scripts/doe-aggregate.test.ts — F-CDF + incomplete-beta vs closed forms/R, planted-effect recovery (p<0.001), null factor (p>0.5), unbalanced cells. HIGH-4 / MED-6 (over-claimed gate guarantees): - ADR-120 gains a "Threat model and limits (honest)" section: gateReExecutes catches a lying log, not a buggy gate (same-process seal+rule). - ADR-118 status reframed to "infrastructure awaiting a validated signal." - tests/unit/validation/rule-consistency.test.ts — property test scoping what the gate actually guarantees (determinism, stable fingerprints, flipped-verdict). - DOE-HARNESS-SCREEN-DESIGN.md — unequal-n note. HIGH-5 / MED-7 (self-fulfilling test / brittle scripts): - tests/unit/learning/mmr-scaling.test.ts — FALSIFIABLE regression for the real O(n^4) scoreHeldOut hang the MMR_POOL_SIZE cap fixed (96s->420ms at n=300). - coupled-anchor.test.ts de-faked: relabeled as a MECHANISM/wiring test, documents that the scripted generator hardcodes the outcome and the real model was an honest null — no longer readable as evidence that coupling helps. - tests/unit/validation/sealed-hash-roundtrip.test.ts — regression for the canonicalJson undefined-key seal bug. Verification: tsc 0 errors; 33/33 new remediation tests + 290/290 full validation+flywheel cluster green. No live model run (Ollama offline + Anthropic account at usage limit) — coupling live-validation surfaced as the one honest open item, not faked. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
144 lines
5.7 KiB
TypeScript
144 lines
5.7 KiB
TypeScript
/**
|
||
* Unit tests for the ADR-122 DoE ANOVA stat helpers (scripts/doe-aggregate.mjs).
|
||
*
|
||
* CRIT-2 guardrail: these lock the statistics to KNOWN closed-form answers and to
|
||
* SYNTHETIC data with a planted truth. If the F-test / incomplete-beta ever drift,
|
||
* the "which factor matters" and "beads: no quality gain" verdicts drift with them,
|
||
* so they are pinned here rather than trusted by inspection.
|
||
*/
|
||
import { describe, it, expect } from 'vitest';
|
||
// @ts-expect-error — .mjs helper module has no type declarations
|
||
import { betai, fPValue, gammaln, anovaMainEffects } from '../../../scripts/doe-aggregate.mjs';
|
||
|
||
describe('gammaln', () => {
|
||
it('matches known factorials: Γ(n) = (n-1)!', () => {
|
||
expect(Math.exp(gammaln(1))).toBeCloseTo(1, 6); // 0! = 1
|
||
expect(Math.exp(gammaln(5))).toBeCloseTo(24, 4); // 4! = 24
|
||
expect(Math.exp(gammaln(6))).toBeCloseTo(120, 3); // 5! = 120
|
||
});
|
||
it('matches Γ(1/2) = √π', () => {
|
||
expect(Math.exp(gammaln(0.5))).toBeCloseTo(Math.sqrt(Math.PI), 6);
|
||
});
|
||
});
|
||
|
||
describe('betai (regularized incomplete beta)', () => {
|
||
it('is 0 at x=0 and 1 at x=1', () => {
|
||
expect(betai(2, 3, 0)).toBe(0);
|
||
expect(betai(2, 3, 1)).toBe(1);
|
||
});
|
||
it('I_x(1,1) = x (uniform CDF)', () => {
|
||
expect(betai(1, 1, 0.2)).toBeCloseTo(0.2, 6);
|
||
expect(betai(1, 1, 0.75)).toBeCloseTo(0.75, 6);
|
||
});
|
||
it('I_x(2,2) = x²(3 − 2x) (closed form)', () => {
|
||
const closed = (x: number) => x * x * (3 - 2 * x);
|
||
expect(betai(2, 2, 0.5)).toBeCloseTo(closed(0.5), 6); // = 0.5
|
||
expect(betai(2, 2, 0.3)).toBeCloseTo(closed(0.3), 6);
|
||
expect(betai(2, 2, 0.9)).toBeCloseTo(closed(0.9), 6);
|
||
});
|
||
it('I_{0.5}(a,a) = 0.5 for any a (symmetry)', () => {
|
||
for (const a of [0.5, 1, 3, 7.5]) expect(betai(a, a, 0.5)).toBeCloseTo(0.5, 6);
|
||
});
|
||
});
|
||
|
||
describe('fPValue (F-distribution upper tail)', () => {
|
||
it('F=1 at equal df gives p ≈ 0.5', () => {
|
||
for (const df of [1, 5, 10, 40]) expect(fPValue(1, df, df)).toBeCloseTo(0.5, 4);
|
||
});
|
||
it('f ≤ 0 gives p = 1; huge F gives p ≈ 0', () => {
|
||
expect(fPValue(0, 3, 20)).toBe(1);
|
||
expect(fPValue(-2, 3, 20)).toBe(1);
|
||
expect(fPValue(1e6, 3, 20)).toBeLessThan(1e-6);
|
||
});
|
||
it('is monotonically decreasing in f', () => {
|
||
const a = fPValue(2, 4, 30);
|
||
const b = fPValue(5, 4, 30);
|
||
const c = fPValue(10, 4, 30);
|
||
expect(a).toBeGreaterThan(b);
|
||
expect(b).toBeGreaterThan(c);
|
||
});
|
||
it('matches a known F critical value: p(F=4.96, df=2,3) ≈ 0.11', () => {
|
||
// Cross-checked against F-table / R pf(4.96,2,3,lower=FALSE) ≈ 0.111.
|
||
expect(fPValue(4.96, 2, 3)).toBeCloseTo(0.111, 2);
|
||
});
|
||
});
|
||
|
||
describe('anovaMainEffects — planted-effect recovery', () => {
|
||
// Synthetic 2×2 factorial: `model` has a LARGE planted effect (pass-rate
|
||
// 0.2 vs 0.8), `feat` has NO effect (identical distribution). 20 reps/cell,
|
||
// within-cell variance present so the error term is well-defined.
|
||
const bits = (ones: number, zeros: number) => [
|
||
...Array(ones).fill(1), ...Array(zeros).fill(0),
|
||
];
|
||
const rows = [
|
||
{ model: 'lo', feat: 'off', reps: bits(4, 16) }, // mean 0.2
|
||
{ model: 'lo', feat: 'on', reps: bits(4, 16) }, // mean 0.2
|
||
{ model: 'hi', feat: 'off', reps: bits(16, 4) }, // mean 0.8
|
||
{ model: 'hi', feat: 'on', reps: bits(16, 4) }, // mean 0.8
|
||
];
|
||
const a = anovaMainEffects(rows, ['model', 'feat']);
|
||
|
||
it('flattens all replicates and finds the grand mean', () => {
|
||
expect(a.N).toBe(80);
|
||
expect(a.grand).toBeCloseTo(0.5, 6);
|
||
expect(a.numCells).toBe(4);
|
||
expect(a.dfError).toBe(76);
|
||
});
|
||
|
||
it('recovers the planted MODEL effect: high F, p < 0.001', () => {
|
||
const m = a.perFactor.model;
|
||
expect(m.df).toBe(1);
|
||
expect(m.levels.find((l: any) => l.level === 'lo').mean).toBeCloseTo(0.2, 6);
|
||
expect(m.levels.find((l: any) => l.level === 'hi').mean).toBeCloseTo(0.8, 6);
|
||
expect(m.F).toBeGreaterThan(20);
|
||
expect(m.p).toBeLessThan(0.001);
|
||
});
|
||
|
||
it('rejects the NULL feat effect: F ≈ 0, p ≈ 1 (not significant)', () => {
|
||
const feat = a.perFactor.feat;
|
||
expect(feat.ss).toBeCloseTo(0, 9);
|
||
expect(feat.F).toBeCloseTo(0, 9);
|
||
expect(feat.p).toBeGreaterThan(0.5);
|
||
});
|
||
|
||
it('reports the design as balanced when reps are equal', () => {
|
||
expect(a.balanced).toBe(true);
|
||
});
|
||
});
|
||
|
||
describe('anovaMainEffects — a random (null) factor stays insignificant', () => {
|
||
// A factor whose levels are assigned by a fixed pseudo-random pattern with the
|
||
// SAME underlying pass-rate should not reach significance.
|
||
const mk = (label: string, pattern: number[]) => ({ noise: label, reps: pattern });
|
||
// Both levels drawn from the same 50/50 mix — no real effect.
|
||
const rows = [
|
||
mk('a', [1, 0, 1, 0, 1, 0, 0, 1]),
|
||
mk('a', [0, 1, 0, 1, 1, 0, 1, 0]),
|
||
mk('b', [1, 0, 0, 1, 0, 1, 1, 0]),
|
||
mk('b', [0, 1, 1, 0, 1, 0, 0, 1]),
|
||
];
|
||
it('null factor has p ≥ 0.05', () => {
|
||
const a = anovaMainEffects(rows, ['noise']);
|
||
expect(a.perFactor.noise.p).toBeGreaterThan(0.05);
|
||
});
|
||
});
|
||
|
||
describe('anovaMainEffects — unbalanced cells handled honestly', () => {
|
||
// opus cell has 7 reps, others 5 → unbalanced. Factor SS still weights by #obs.
|
||
const rows = [
|
||
{ model: 'qwen', reps: [0, 0, 1, 0, 0] }, // n=5
|
||
{ model: 'opus', reps: [1, 1, 1, 0, 1, 1, 1] }, // n=7
|
||
];
|
||
const a = anovaMainEffects(rows, ['model']);
|
||
it('flags the design as unbalanced', () => {
|
||
expect(a.balanced).toBe(false);
|
||
expect(a.N).toBe(12);
|
||
});
|
||
it('weights level means by their own replicate counts', () => {
|
||
const m = a.perFactor.model;
|
||
expect(m.levels.find((l: any) => l.level === 'qwen').n).toBe(5);
|
||
expect(m.levels.find((l: any) => l.level === 'opus').n).toBe(7);
|
||
expect(m.levels.find((l: any) => l.level === 'opus').mean).toBeCloseTo(6 / 7, 6);
|
||
});
|
||
});
|