Context
packages/loopover-engine/src/signals/contributor-calibration.ts's applyContributorCalibration(baselineReadinessScore, calibration) adjusts a baseline readiness score by a contributor's own predict-vs-real calibration history. Its own field-level doc comment on ContributorCalibrationSignal.agreementRate states: "Clamped to [0, 1] before use, so a malformed upstream aggregate can never push the adjustment past its own clamp" — i.e. the function's documented contract is that it degrades gracefully for any malformed calibration input and always returns a valid [0, 100] score (or the unchanged baseline).
The cold-start guard on line 56 is if (!calibration || calibration.sampleSize < MIN_CALIBRATION_SAMPLES) return baselineReadinessScore;. This relies on JavaScript's < comparison, which is always false for NaN. A calibration.sampleSize of NaN therefore bypasses the cold-start guard entirely instead of being treated as "insufficient history."
The local clamp(value, min, max) helper on line 18 (return Math.min(max, Math.max(min, value));) also has no Number.isFinite guard, unlike its sibling normalizers elsewhere in this package (e.g. governor/reputation-throttle.ts's clampFraction, which is Number.isFinite(value) ? Math.min(1, Math.max(0, value)) : fallback, or governor/rate-limit.ts/governor/budget-cap.ts's finiteNonNegativeInt/finiteNonNegative, both of which explicitly fall back to a safe default for non-finite input). Math.max(min, NaN) and Math.min(max, NaN) both evaluate to NaN, so a NaN agreementRate (or a NaN produced anywhere downstream) propagates straight through clamp() unclamped.
Verified empirically (node -e, using the exact logic from the file):
applyContributorCalibration(70, { sampleSize: NaN, agreementRate: 0.9 }) returns 78 — the cold-start guard is bypassed and a full calibration adjustment is applied from a malformed sample count instead of returning the unchanged baseline of 70.
applyContributorCalibration(70, { sampleSize: 20, agreementRate: NaN }) returns NaN — a non-numeric readiness score reaches the caller, violating the documented [0, 100]-clamped contract.
The existing test suite (test/unit/predicted-gate.test.ts, describe("applyContributorCalibration (#2349)")) has explicit "CLAMP BOUNDARY" tests for out-of-range finite values (agreementRate: 1.7, agreementRate: -0.4) but no test exercises a non-finite (NaN) sampleSize or agreementRate.
Requirements
packages/loopover-engine/src/signals/contributor-calibration.ts's clamp(value, min, max) helper must treat a non-finite value (checked via Number.isFinite) as min (i.e. the lowest end of the clamp range) rather than propagating NaN, mirroring the fail-toward-the-safe-end pattern already used by this package's other numeric normalizers (governor/reputation-throttle.ts's clampFraction, governor/rate-limit.ts's finiteNonNegativeInt).
applyContributorCalibration's cold-start guard must treat a non-finite calibration.sampleSize as "insufficient history" (i.e. return baselineReadinessScore unchanged), not fall through to computing an adjustment. Change the guard's condition so calibration.sampleSize < MIN_CALIBRATION_SAMPLES is only trusted when Number.isFinite(calibration.sampleSize) is true; a non-finite sampleSize must take the same "cold start" branch.
- After this fix,
applyContributorCalibration(70, { sampleSize: NaN, agreementRate: 0.9 }) must return 70 (the unchanged baseline), and applyContributorCalibration(70, { sampleSize: 20, agreementRate: NaN }) must return a finite number in [0, 100] (not NaN).
- Do not change the function's behavior for any already-tested finite input (the existing "CLAMP BOUNDARY" tests in
test/unit/predicted-gate.test.ts must continue to pass unchanged).
Deliverables
Test Coverage Requirements
This repo's Codecov patch gate is 99%+ for src/**/packages/**. Both new branches (non-finite sampleSize, non-finite agreementRate) must be covered by new assertions in test/unit/predicted-gate.test.ts.
Expected Outcome
applyContributorCalibration can never return NaN, and a malformed/non-finite sampleSize is treated as insufficient calibration history (baseline returned unchanged) rather than silently applying a calibration adjustment — matching the function's own documented "malformed upstream aggregate can never push the adjustment past its own clamp" contract.
Links & Resources
packages/loopover-engine/src/signals/contributor-calibration.ts
test/unit/predicted-gate.test.ts (describe("applyContributorCalibration (#2349)"), starting around line 556)
- Precedent for the fail-toward-safe-end normalization pattern:
packages/loopover-engine/src/governor/reputation-throttle.ts's clampFraction, packages/loopover-engine/src/governor/rate-limit.ts's finiteNonNegativeInt
Context
packages/loopover-engine/src/signals/contributor-calibration.ts'sapplyContributorCalibration(baselineReadinessScore, calibration)adjusts a baseline readiness score by a contributor's own predict-vs-real calibration history. Its own field-level doc comment onContributorCalibrationSignal.agreementRatestates: "Clamped to [0, 1] before use, so a malformed upstream aggregate can never push the adjustment past its own clamp" — i.e. the function's documented contract is that it degrades gracefully for any malformedcalibrationinput and always returns a valid[0, 100]score (or the unchanged baseline).The cold-start guard on line 56 is
if (!calibration || calibration.sampleSize < MIN_CALIBRATION_SAMPLES) return baselineReadinessScore;. This relies on JavaScript's<comparison, which is alwaysfalseforNaN. Acalibration.sampleSizeofNaNtherefore bypasses the cold-start guard entirely instead of being treated as "insufficient history."The local
clamp(value, min, max)helper on line 18 (return Math.min(max, Math.max(min, value));) also has noNumber.isFiniteguard, unlike its sibling normalizers elsewhere in this package (e.g.governor/reputation-throttle.ts'sclampFraction, which isNumber.isFinite(value) ? Math.min(1, Math.max(0, value)) : fallback, orgovernor/rate-limit.ts/governor/budget-cap.ts'sfiniteNonNegativeInt/finiteNonNegative, both of which explicitly fall back to a safe default for non-finite input).Math.max(min, NaN)andMath.min(max, NaN)both evaluate toNaN, so aNaNagreementRate(or aNaNproduced anywhere downstream) propagates straight throughclamp()unclamped.Verified empirically (
node -e, using the exact logic from the file):applyContributorCalibration(70, { sampleSize: NaN, agreementRate: 0.9 })returns78— the cold-start guard is bypassed and a full calibration adjustment is applied from a malformed sample count instead of returning the unchanged baseline of70.applyContributorCalibration(70, { sampleSize: 20, agreementRate: NaN })returnsNaN— a non-numeric readiness score reaches the caller, violating the documented[0, 100]-clamped contract.The existing test suite (
test/unit/predicted-gate.test.ts,describe("applyContributorCalibration (#2349)")) has explicit "CLAMP BOUNDARY" tests for out-of-range finite values (agreementRate: 1.7,agreementRate: -0.4) but no test exercises a non-finite (NaN)sampleSizeoragreementRate.Requirements
packages/loopover-engine/src/signals/contributor-calibration.ts'sclamp(value, min, max)helper must treat a non-finitevalue(checked viaNumber.isFinite) asmin(i.e. the lowest end of the clamp range) rather than propagatingNaN, mirroring the fail-toward-the-safe-end pattern already used by this package's other numeric normalizers (governor/reputation-throttle.ts'sclampFraction,governor/rate-limit.ts'sfiniteNonNegativeInt).applyContributorCalibration's cold-start guard must treat a non-finitecalibration.sampleSizeas "insufficient history" (i.e. returnbaselineReadinessScoreunchanged), not fall through to computing an adjustment. Change the guard's condition socalibration.sampleSize < MIN_CALIBRATION_SAMPLESis only trusted whenNumber.isFinite(calibration.sampleSize)istrue; a non-finitesampleSizemust take the same "cold start" branch.applyContributorCalibration(70, { sampleSize: NaN, agreementRate: 0.9 })must return70(the unchanged baseline), andapplyContributorCalibration(70, { sampleSize: 20, agreementRate: NaN })must return a finite number in[0, 100](notNaN).test/unit/predicted-gate.test.tsmust continue to pass unchanged).Deliverables
clamp()inpackages/loopover-engine/src/signals/contributor-calibration.tsguards against non-finite input.applyContributorCalibration's cold-start guard treats a non-finitesampleSizeas insufficient history.test/unit/predicted-gate.test.ts(alongside the existingapplyContributorCalibration (#2349)describe block) covering:NaNsampleSizewith a finiteagreementRate, and a finitesampleSizewith aNaNagreementRate.Test Coverage Requirements
This repo's Codecov patch gate is 99%+ for
src/**/packages/**. Both new branches (non-finitesampleSize, non-finiteagreementRate) must be covered by new assertions intest/unit/predicted-gate.test.ts.Expected Outcome
applyContributorCalibrationcan never returnNaN, and a malformed/non-finitesampleSizeis treated as insufficient calibration history (baseline returned unchanged) rather than silently applying a calibration adjustment — matching the function's own documented "malformed upstream aggregate can never push the adjustment past its own clamp" contract.Links & Resources
packages/loopover-engine/src/signals/contributor-calibration.tstest/unit/predicted-gate.test.ts(describe("applyContributorCalibration (#2349)"), starting around line 556)packages/loopover-engine/src/governor/reputation-throttle.ts'sclampFraction,packages/loopover-engine/src/governor/rate-limit.ts'sfiniteNonNegativeInt