Skip to content

[Bug]: upstream-constant parser truncates fractional digit-grouping (0.000_001 -> 0) #992

Description

@galuis116

[Bug]: upstream-constant parser truncates fractional digit-grouping → 0.000_001 reads as 0 (skews scoring)

Summary

#969 ("parse underscore separators and scientific notation in upstream
constants") fixed the Python-numeric-literal parser to accept underscores in the
integer part (1_500_000) and exponents (1e-9) — but it left the
fractional part underscore-blind. Per PEP 515, underscores may appear between
digits in any part of a numeric literal, including after the decimal point. So
a valid upstream constant like RATE = 0.000_001 matches only up to the first
fractional underscore and is read as 0 — a silent truncation that turns a
small rate/share into zero (a divide/multiply-by-zero hazard that skews every
score preview and breakdown).

Evidence

// src/scoring/model.ts:159 — parsePythonNumberConstants (introduced by #969)
const match = line.match(/^([A-Z][A-Z0-9_]+)\s*=\s*([-+]?(?:\d[\d_]*\.?\d*|\.\d+)(?:[eE][-+]?\d+)?)/);
//                                                          ^^^^^^^ int part allows _   ^^^ frac part: \d* (NO _)   ^^^^ bare-decimal: \d+ (NO _)

The integer part is \d[\d_]* (underscores allowed), but the fractional part is
\d* and the bare-decimal alternative is \.\d+ — neither permits _. Verified
by running the exact regex + Number(raw.replace(/_/g, "")):

Python literal (valid PEP 515) True value Parser yields
RATE = 0.000_001 0.000001 0
SCALE = 3.14_15 3.1415 3.14
VAL = 1_000.000_5 1000.0005 1000

(Integer grouping 1_500_000 and exponents 5.8e1/1e-9 parse correctly — only
fractional grouping is broken.)

Reachability

Real trigger. refreshScoringModelSnapshot (model.ts:~90) fetches upstream
gittensor/constants.py and runs it through parsePythonNumberConstants, merging
the result into the scoring constants that feed buildScorePreview (and the
score breakdown / unmodeled-constant detection). An upstream maintainer writing a
small rate/share with fractional digit-grouping — exactly the readability feature
#969 set out to support — gets that constant read as a truncated or zeroed value.
A 0.000_001-style multiplier/divisor collapsing to 0 is a concrete zero hazard
in scoring math, and because the same parser drives findUnmodeledUpstreamConstants,
the wrong value is silently used rather than flagged.

Suggested fix

Extend the fractional digit classes to allow _ (and require a leading digit in
the bare-decimal branch), mirroring the integer part:

const match = line.match(/^([A-Z][A-Z0-9_]+)\s*=\s*([-+]?(?:\d[\d_]*\.?[\d_]*|\.\d[\d_]*)(?:[eE][-+]?\d+)?)/);

Number(raw.replace(/_/g, "")) already strips the underscores before parsing, so
no other change is needed. Verified this regex parses all of 0.000_001,
3.14_15, 1_000.000_5 correctly and leaves every previously-passing case
unchanged (1_500_000, 5.8e1, 1e-9, -2.5e-3, 0.90, 1.15, .5, 5).

Test status

Not covered. The #969 regression test (test/unit/scoring.test.ts, "parses
underscore separators, floats, and scientific notation without truncating") covers
1_500_000, 5.8e1, 1e-9, and a plain 0.90none has an underscore in the
fractional digits
, so the truncation ships green. A regression test should assert
parsePythonNumberConstants("RATE = 0.000_001\n") yields { RATE: 0.000001 }.

Confidence note

High. The truncation is proven by executing the exact merged regex on valid
PEP 515 literals, the parser is demonstrably on the scoring-snapshot path, and the
fix is a minimal, fully-verified regex change. It is an incomplete-fix
regression
: #969 added underscore support but only for the integer part, leaving
the identical feature broken for the fractional part — and its own test omits that
case.

Distinct from prior reports

Directly continues #969/#810 (the same parser) but covers the unaddressed
fractional-underscore case. Unrelated to the suspended-installation scope (#953),
predicted-gate, gate-403, or BYOK.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.slopAI slop and/or attempts to game additional points via manipulation or alt profiles.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions