f4f51ed7ca
Step 35 asks the deployed process to prove WHICH frozen curve it resolves — artifact id, procedure, era, source digest, knot digest, curve digest, support, servable, stage — with the shadow still OFF. The probe could not answer, so the same shadowState() evaluator now resolves the promoted artifact and reports its identity. Identity only: the curve is committed and addressable by artifact_id, and repeating it on a status page would put a second copy of the truth there. null means nothing is promoted, which is also the honest answer when the file is missing or fails its policy at load. Suite 404/404. Shadow OFF. Live OFF. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CQJeAG8vcDoL5zkiaJyVb8
201 lines
9.8 KiB
JavaScript
201 lines
9.8 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* A VALIDATOR IS NOT A REPAIR. `servable:false` must mean something mechanically.
|
|
*
|
|
* The previous release detected the violation correctly and then served the
|
|
* certified state anyway. These tests exist so that cannot recur.
|
|
*/
|
|
const pc = require('../../src/services/model/probabilityContract');
|
|
const svc = require('../../src/services/model/probabilityContractService');
|
|
const registry = require('../../src/services/model/artifactRegistry');
|
|
const fp = require('../../src/services/model/fitPolicy');
|
|
|
|
const ERA = 'engine1@2026-08-07-fullwindow';
|
|
const good = registry.load('mlb', 'hits');
|
|
const read = (p) => ({ sport: 'mlb', stat: 'hits', model_version: ERA, p_win: p });
|
|
const est = () => 0.6;
|
|
|
|
describe('an unservable artifact can never emit a certified state', () => {
|
|
// Era and estimator mismatch resolve to VERSION_MISMATCH rather than the new
|
|
// state — that is DELIBERATE. "This artifact belongs to a different
|
|
// forecaster" is a more precise thing to say than "policy blocked", and the
|
|
// existing state already says it exactly. The invariant asserted for all six
|
|
// is the one that matters: never certified, never a number.
|
|
const violations = {
|
|
ERA_NOT_RESTRICTED: [{ era_audit: { era_counts: { [ERA]: 10, 'engine1@2026-07-20': 5 }, wrong_era_rows: 5 } }, 'ARTIFACT_POLICY_BLOCKED'],
|
|
MODEL_VERSION_MISMATCH: [{ model_version: 'engine1@2026-07-20' }, 'VERSION_MISMATCH'],
|
|
ESTIMATOR_MISMATCH: [{ estimator_type: 'low_param' }, 'VERSION_MISMATCH'],
|
|
MISSING_IDENTITY: [{ knot_digest: null }, 'ARTIFACT_POLICY_BLOCKED'],
|
|
THIN_FIT: [{ fit_n: 3 }, 'ARTIFACT_POLICY_BLOCKED'],
|
|
SUPPORT_WIDENING: [{ certified_bands: [[0.50, 0.99]] }, 'ARTIFACT_POLICY_BLOCKED'],
|
|
};
|
|
|
|
for (const [name, [over, expected]] of Object.entries(violations)) {
|
|
it(`${name} -> ${expected}, never CERTIFIED_CALIBRATED`, () => {
|
|
const base = { ...good, ...over };
|
|
const check = fp.validate(base, { era_counts: base.era_audit ? base.era_audit.era_counts : null });
|
|
expect(check.servable).toBe(false);
|
|
const artifact = { ...base, servable: check.servable, fit_policy_violations: check.violations };
|
|
const r = pc.resolve(read(0.65), { estimate: est, artifact });
|
|
expect(r.probability_state).toBe(pc.STATE[expected]);
|
|
expect(r.probability_state).not.toBe(pc.STATE.CERTIFIED_CALIBRATED);
|
|
expect(r.served_probability).toBeNull();
|
|
expect(r.raw_model_probability).toBe(0.65); // raw is still evidence
|
|
expect(pc.derivedClaims(r, -115).available).toBe(false);
|
|
});
|
|
}
|
|
|
|
it('and every probability-derived claim is withheld with it', () => {
|
|
const artifact = { ...good, servable: false, fit_policy_violations: ['ERA_NOT_RESTRICTED'] };
|
|
const d = pc.derivedClaims(pc.resolve(read(0.65), { estimate: est, artifact }), -115);
|
|
expect(d.available).toBe(false);
|
|
expect(d.ev_pct).toBeNull();
|
|
expect(d.kelly).toBeNull();
|
|
expect(d.value).toBeNull();
|
|
});
|
|
|
|
it('the blocked state names the violation rather than shrugging', () => {
|
|
const artifact = { ...good, servable: false, fit_policy_violations: ['ERA_NOT_RESTRICTED'] };
|
|
expect(pc.resolve(read(0.65), { estimate: est, artifact }).reason).toContain('ERA_NOT_RESTRICTED');
|
|
});
|
|
});
|
|
|
|
describe('no environment variable can override the gate', () => {
|
|
const artifact = { ...good, servable: false, fit_policy_violations: ['ERA_NOT_RESTRICTED'] };
|
|
|
|
it('shadow ON does not make an unservable artifact certify', () => {
|
|
expect(pc.shadowState({ PROBABILITY_CONTRACT_SHADOW: '1' }).shadow).toBe('ON');
|
|
// the gate lives in the resolution, not beside the flag
|
|
const r = pc.resolve(read(0.65), { estimate: est, artifact });
|
|
expect(r.probability_state).toBe(pc.STATE.ARTIFACT_POLICY_BLOCKED);
|
|
});
|
|
|
|
it('resolve takes no flag, so there is nothing for a flag to reach', () => {
|
|
const src = require('fs').readFileSync(
|
|
require('path').join(__dirname, '../../src/services/model/probabilityContract.js'), 'utf8');
|
|
const body = src.slice(src.indexOf('function resolve'), src.indexOf('const isCertified'));
|
|
expect(body).not.toContain('process.env');
|
|
expect(body).not.toContain('PROBABILITY_CONTRACT_SHADOW');
|
|
});
|
|
});
|
|
|
|
describe('one validity decision for shadow AND live', () => {
|
|
it('a shadow-approved artifact is refused for live use', () => {
|
|
expect(good.approved_for_shadow).toBe(true);
|
|
expect(good.approved_for_live).toBe(false);
|
|
const live = pc.resolve(read(0.65), { estimate: est, artifact: good, usage: 'live' });
|
|
expect(live.probability_state).toBe(pc.STATE.ARTIFACT_POLICY_BLOCKED);
|
|
expect(live.reason).toContain('not promoted for live');
|
|
const shadow = pc.resolve(read(0.65), { estimate: est, artifact: good, usage: 'shadow' });
|
|
expect(shadow.probability_state).toBe(pc.STATE.CERTIFIED_CALIBRATED);
|
|
});
|
|
|
|
it('live consumes the SAME servable decision, not a parallel one', () => {
|
|
const bad = { ...good, servable: false, approved_for_live: true, fit_policy_violations: ['X'] };
|
|
expect(pc.resolve(read(0.65), { estimate: est, artifact: bad, usage: 'live' }).probability_state)
|
|
.toBe(pc.STATE.ARTIFACT_POLICY_BLOCKED);
|
|
});
|
|
});
|
|
|
|
describe('the active curve does not move', () => {
|
|
it('the runtime holds no fitter — nothing to refit on a snapshot', () => {
|
|
const src = require('fs').readFileSync(
|
|
require('path').join(__dirname, '../../src/services/model/probabilityContractService.js'), 'utf8');
|
|
const code = src.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
|
|
expect(code).not.toContain('fitIsotonic');
|
|
expect(code).not.toContain('fromLedger');
|
|
expect(code).not.toContain('loadRows');
|
|
});
|
|
|
|
it('repeated builds return the same artifact object and the same numbers', async () => {
|
|
const a = await svc.build(null, { sport: 'mlb', stat: 'hits' });
|
|
const b = await svc.build(null, { sport: 'mlb', stat: 'hits' });
|
|
expect(a.artifact).toBe(b.artifact); // cached, identical identity
|
|
expect(a.artifact.source_digest).toBe(b.artifact.source_digest);
|
|
expect(a.resolve(read(0.72)).served_probability).toBe(b.resolve(read(0.72)).served_probability);
|
|
});
|
|
|
|
it('a new settled outcome cannot alter the curve — it is a committed file', () => {
|
|
const before = registry.applyCurve(good, 0.65);
|
|
registry.__resetCache();
|
|
const after = registry.applyCurve(registry.load('mlb', 'hits'), 0.65);
|
|
expect(after).toBe(before);
|
|
});
|
|
});
|
|
|
|
describe('promotion is a deliberate act', () => {
|
|
it('the promotion table is a frozen source constant, not runtime state', () => {
|
|
expect(Object.isFrozen(registry.PROMOTED)).toBe(true);
|
|
expect(Object.isFrozen(registry.PROMOTED['mlb:hits'])).toBe(true);
|
|
// strict mode makes the write throw rather than fail silently — either way
|
|
// the table is unchanged, which is the property under test
|
|
expect(() => { registry.PROMOTED['mlb:rbi'] = { artifact_id: 'x', stage: 'APPROVED_FOR_LIVE' }; }).toThrow();
|
|
expect(registry.PROMOTED['mlb:rbi']).toBeUndefined();
|
|
});
|
|
|
|
it('an artifact file that exists but is not named is NOT loaded', () => {
|
|
// load() resolves the file FROM the promotion table, so an unnamed artifact
|
|
// sitting in the directory is inert.
|
|
expect(registry.load('mlb', 'rbi')).toBeNull();
|
|
});
|
|
|
|
it('the loaded artifact id must equal the promoted id', () => {
|
|
expect(registry.load('mlb', 'hits').artifact_id).toBe(registry.PROMOTED['mlb:hits'].artifact_id);
|
|
});
|
|
|
|
it('a file declaring a DIFFERENT id is refused — dropping one in promotes nothing', () => {
|
|
const promoted = registry.PROMOTED['mlb:hits'];
|
|
expect(registry.acceptFile({ artifact_id: promoted.artifact_id }, promoted)).toBe(true);
|
|
// the case load() can never reach on the happy path, and the reason the
|
|
// guard existed unprotected until a teeth injection came back green
|
|
expect(registry.acceptFile({ artifact_id: 'mlb-hits-isotonic@2099-01-01' }, promoted)).toBe(false);
|
|
expect(registry.acceptFile({ artifact_id: null }, promoted)).toBe(false);
|
|
expect(registry.acceptFile({}, promoted)).toBe(false);
|
|
expect(registry.acceptFile(null, promoted)).toBe(false);
|
|
expect(registry.acceptFile({ artifact_id: 'x' }, null)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('model-era transition law', () => {
|
|
it('a NEW model era does not inherit this artifact', () => {
|
|
const r = pc.resolve({ sport: 'mlb', stat: 'hits', model_version: 'engine1@2027-01-01', p_win: 0.65 },
|
|
{ estimate: est, artifact: good });
|
|
expect(r.probability_state).toBe(pc.STATE.VERSION_MISMATCH);
|
|
expect(r.served_probability).toBeNull();
|
|
});
|
|
|
|
it('and an artifact relabelled to a new era fails its own policy', () => {
|
|
const relabelled = { ...good, model_version: 'engine1@2027-01-01' };
|
|
const check = fp.validate(relabelled, { era_counts: good.era_audit.era_counts });
|
|
expect(check.valid).toBe(false);
|
|
expect(check.servable).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('the runtime can name its artifact with the shadow OFF', () => {
|
|
it('reports the full identity without enabling anything', () => {
|
|
const st = pc.shadowState({});
|
|
expect(st.shadow).toBe('OFF');
|
|
expect(st.live_serving).toBe('OFF');
|
|
const a = st.artifact;
|
|
for (const k of ['artifact_id', 'procedure_version', 'model_version', 'fit_as_of',
|
|
'training_cutoff', 'fit_n', 'source_digest', 'knot_digest', 'served_curve_digest',
|
|
'certified_bands', 'stage']) expect(a[k]).toBeTruthy();
|
|
expect(a.servable).toBe(true);
|
|
expect(a.approved_for_shadow).toBe(true);
|
|
expect(a.approved_for_live).toBe(false);
|
|
expect(a.wrong_era_rows).toBe(0);
|
|
expect(a.withheld_from_fit).toBe(0);
|
|
});
|
|
|
|
it('does not put a second copy of the curve on a status surface', () => {
|
|
expect(pc.shadowState({}).artifact.served_curve).toBeUndefined();
|
|
});
|
|
|
|
it('two resolutions return the same artifact — no refit between calls', () => {
|
|
expect(JSON.stringify(pc.shadowState({}).artifact))
|
|
.toBe(JSON.stringify(pc.shadowState({}).artifact));
|
|
});
|
|
});
|