Read integrity, as-of context, and the shadow matchup resolve (A1-A7)
Seven orders of measurement-first repair. The served grade does not move. A0/A1 — the unordered page walk returned the right COUNT and the wrong ROWS: 410-617 of 2,490 duplicated with an equal number never returned, while rows.length matched the server exactly. safePaginate orders on a real unique key, verifies the tuple at runtime, and THROWS on a query error instead of treating it as end-of-data. Both hits PROVES are withdrawn: they were drawn through that reader, and defense_by_direction's distinct-n was likely below the gate floor all along. A2/A2b — rolled across every reader: 11 FAIL -> 0. Composite keys pulled from pg_index (the context tables are dated-composite and had no single unique column). The unordered helper is deleted, not parked. A3 — ledgerService and retentionService defaulted the SAME env var to DIFFERENT versions, so no ledger row ever carried the marker eligibility requires. One source now. model_snapshots settlement moved onto the cron: 15,484 -> 28,894 settled, repaired-champion 0 -> 7,556. A4 — hitsFactorContext takes an as-of cutoff. Refusal over reconstruction: no row at-or-before the date means the factor does not apply, never the nearest row. Live path unchanged, proven 400/400 on real rows. A5 — factor_inputs freezes what the factor READ, never the multiplier, so an audit can recompute and check. It also recorded the finding: the three hits factors have NEVER fired. prop.opponent and prop.opposing_pitcher are read by the resolver and written by nothing. A6/A7 — matchupKeys resolves those keys from the posted lineup plus the schedule's probable pitchers, and fires the factors into a SHADOW freeze: 248 fires on 308 props, 245 of which would move the grade. The served forecast is untouched. specs/a8-shadow-factor-gate.md pre-registers the test that decides whether they ever go live. Nothing is turned on. CALIBRATION_DEPLOYED stays []. Both verdicts stay withdrawn. 4,772 tests / 371 suites green, web build exit 0, read-integrity harness 34/34. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -56,9 +56,10 @@ function etDateOf(iso) {
|
||||
* forecast, and never on a mixture of the two, which is the trap that would
|
||||
* otherwise be invisible once both generations sit in the same table.
|
||||
*/
|
||||
const MODEL_VERSION = process.env.MODEL_VERSION || 'engine1@2026-08-07-fullwindow';
|
||||
/** Rows at or after this marker are eligible for forward re-audit. */
|
||||
const REPAIRED_CHAMPION_VERSION = 'engine1@2026-08-07-fullwindow';
|
||||
// FIX A3 — ONE SOURCE (src/config/modelVersion.js). Re-exported so existing
|
||||
// importers keep working, but never re-declared: the twin default in
|
||||
// ledgerService is exactly how these two drifted apart.
|
||||
const { MODEL_VERSION, REPAIRED_CHAMPION_VERSION } = require('../config/modelVersion');
|
||||
|
||||
function codeSha() {
|
||||
return process.env.SOURCE_COMMIT || process.env.GIT_SHA || process.env.COOLIFY_GIT_COMMIT_SHA || null;
|
||||
@@ -149,6 +150,12 @@ function rowsFromSides(base, sides, ctx = {}) {
|
||||
// The counterfactual enabler. Absent on pre-feature refusals (the juice
|
||||
// gate runs before features are computed) — honestly null, never faked.
|
||||
features: s._features && Object.keys(s._features).length ? s._features : null,
|
||||
|
||||
// FIX A5 — the RAW inputs the hits factors read, frozen at grade time so a
|
||||
// re-audit reads evidence instead of reconstructing context. Inputs only:
|
||||
// the multiplier is recomputable from them and is deliberately not stored.
|
||||
// Null on every non-hits row and on any row with no factor context.
|
||||
factor_inputs: s.factor_inputs || null,
|
||||
});
|
||||
}
|
||||
return rows;
|
||||
|
||||
Reference in New Issue
Block a user