diff --git a/src/scoring/model.ts b/src/scoring/model.ts index c088a2abcd..07b391a089 100644 --- a/src/scoring/model.ts +++ b/src/scoring/model.ts @@ -153,10 +153,10 @@ export async function getOrCreateScoringModelSnapshot(env: Env): Promise { const constants: Record = {}; for (const line of source.split("\n")) { - // Match Python numeric literals including underscore separators (1_000_000), floats, and exponents - // (1e-9, 5.8e1). The previous /[-+]?\d+(?:\.\d+)?/ stopped at `_`/`e`, truncating 1_000_000 -> 1 and - // 1e-9 -> 1, which silently misparsed any such upstream constant and polluted the unmodeled list (#810). - const match = line.match(/^([A-Z][A-Z0-9_]+)\s*=\s*([-+]?(?:\d[\d_]*\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)/); + // Match Python numeric literals including underscore separators in integer and fractional parts + // (1_000_000, 0.000_001, 3.14_15), floats, and exponents (1e-9, 5.8e1). The previous regex only + // allowed `_` in the integer part, truncating 0.000_001 -> 0 and 3.14_15 -> 3.14 (#992). + const match = line.match(/^([A-Z][A-Z0-9_]+)\s*=\s*([-+]?(?:\d[\d_]*\.?[\d_]*|\.\d[\d_]*)(?:[eE][-+]?\d+)?)/); if (!match) continue; const name = match[1]!; const raw = match[2]!; diff --git a/test/unit/scoring.test.ts b/test/unit/scoring.test.ts index 8f08ac8b94..f589670214 100644 --- a/test/unit/scoring.test.ts +++ b/test/unit/scoring.test.ts @@ -86,6 +86,22 @@ OSS_EMISSION_SHARE = 0.90 expect(parsed.OSS_EMISSION_SHARE).toBe(0.9); }); + it("parses underscore separators in the fractional part of upstream constants (#992)", () => { + const parsed = parsePythonNumberConstants( + ` +RATE = 0.000_001 +SCALE = 3.14_15 +VAL = 1_000.000_5 +BARE = .5_0 +`, + { knownOnly: false }, + ); + expect(parsed.RATE).toBe(0.000001); + expect(parsed.SCALE).toBe(3.1415); + expect(parsed.VAL).toBe(1000.0005); + expect(parsed.BARE).toBe(0.5); + }); + it("flags only scoring snapshots older than the freshness window as stale (#810)", () => { const now = Date.parse("2026-06-21T12:00:00.000Z"); const justFresh = new Date(now - SCORING_SNAPSHOT_STALE_MS + 60_000).toISOString();