The runtime names its artifact before anything is switched on
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
This commit is contained in:
@@ -305,6 +305,31 @@ const SHADOW_ENV = 'PROBABILITY_CONTRACT_SHADOW';
|
|||||||
function shadowState(env = process.env) {
|
function shadowState(env = process.env) {
|
||||||
const raw = env[SHADOW_ENV];
|
const raw = env[SHADOW_ENV];
|
||||||
const on = String(raw || '') === '1';
|
const on = String(raw || '') === '1';
|
||||||
|
// WHICH FROZEN ARTIFACT THIS PROCESS WOULD USE — resolvable with the shadow
|
||||||
|
// OFF, because "is the right curve deployed?" must be answerable before
|
||||||
|
// anything is switched on. Identity only: the curve itself is committed and
|
||||||
|
// addressable by `artifact_id`, and repeating it here would put a second copy
|
||||||
|
// of the truth on a status page.
|
||||||
|
let artifact = null;
|
||||||
|
try {
|
||||||
|
const a = require('./artifactRegistry').load(MLB_HITS.sport, MLB_HITS.stat);
|
||||||
|
if (a) {
|
||||||
|
artifact = Object.freeze({
|
||||||
|
artifact_id: a.artifact_id, procedure_version: a.procedure_version,
|
||||||
|
model_version: a.model_version, fit_as_of: a.fit_as_of,
|
||||||
|
training_cutoff: a.training_cutoff, fit_n: a.fit_n,
|
||||||
|
withheld_from_fit: a.withheld_from_fit,
|
||||||
|
source_digest: a.source_digest, knot_digest: a.knot_digest,
|
||||||
|
served_curve_digest: a.served_curve_digest, knot_count: a.knot_count,
|
||||||
|
certified_bands: a.certified_bands, stage: a.stage,
|
||||||
|
approved_for_shadow: a.approved_for_shadow, approved_for_live: a.approved_for_live,
|
||||||
|
servable: a.servable, fit_policy_valid: a.fit_policy_valid,
|
||||||
|
fit_policy_violations: a.fit_policy_violations,
|
||||||
|
wrong_era_rows: a.era_audit ? a.era_audit.wrong_era_rows : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch { artifact = null; }
|
||||||
|
|
||||||
return Object.freeze({
|
return Object.freeze({
|
||||||
shadow: on ? 'ON' : 'OFF',
|
shadow: on ? 'ON' : 'OFF',
|
||||||
env_var: SHADOW_ENV,
|
env_var: SHADOW_ENV,
|
||||||
@@ -315,6 +340,9 @@ function shadowState(env = process.env) {
|
|||||||
certified_support: MLB_HITS.certified_bands,
|
certified_support: MLB_HITS.certified_bands,
|
||||||
estimator_version: MLB_HITS.estimator_version,
|
estimator_version: MLB_HITS.estimator_version,
|
||||||
model_version: MLB_HITS.model_version,
|
model_version: MLB_HITS.model_version,
|
||||||
|
// null means NOTHING is promoted — which is also the honest answer when the
|
||||||
|
// file is missing or fails its policy at load.
|
||||||
|
artifact,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,3 +172,29 @@ describe('model-era transition law', () => {
|
|||||||
expect(check.servable).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));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user