Summary
In validator/app/src/compute_horde_validator/validator/scoring/calculations.py, the function score_organic_jobs (lines ~167-180) contains a variable-shadowing bug that silently disables the organic job score cap configured via DYNAMIC_SCORE_ORGANIC_JOBS_LIMIT.
Root Cause
def score_organic_jobs(jobs: Sequence[OrganicJob]) -> dict[str, float]:
batch_scores: defaultdict[str, float] = defaultdict(float)
score = get_config("DYNAMIC_ORGANIC_JOB_SCORE") # per-job score (e.g. 1.0)
limit = get_config("DYNAMIC_SCORE_ORGANIC_JOBS_LIMIT")
for job in jobs:
batch_scores[job.miner.hotkey] += score
if limit >= 0:
for hotkey, score in batch_scores.items(): # ← shadows outer `score`!
batch_scores[hotkey] = min(score, limit * score) # always = score (no cap)
The for hotkey, score in batch_scores.items() loop rebinds score to the accumulated total for each hotkey. Inside the loop, min(score, limit * score) where score is already the accumulated total — not the per-job score — evaluates to min(total, limit * total). For any limit >= 1 this always equals total, so the cap is never applied.
Impact
DYNAMIC_SCORE_ORGANIC_JOBS_LIMIT is entirely ignored whenever limit >= 0. Miners can accumulate organic-job scores without bound regardless of the configured cap.
Fix
per_job_score = get_config("DYNAMIC_ORGANIC_JOB_SCORE")
limit = get_config("DYNAMIC_SCORE_ORGANIC_JOBS_LIMIT")
for job in jobs:
batch_scores[job.miner.hotkey] += per_job_score
if limit >= 0:
cap = per_job_score * limit
for hotkey, accumulated in batch_scores.items():
batch_scores[hotkey] = min(accumulated, cap)
A fix with 12 unit tests (including a regression test) is available in PR #[to be linked].
Summary
In
validator/app/src/compute_horde_validator/validator/scoring/calculations.py, the functionscore_organic_jobs(lines ~167-180) contains a variable-shadowing bug that silently disables the organic job score cap configured viaDYNAMIC_SCORE_ORGANIC_JOBS_LIMIT.Root Cause
The
for hotkey, score in batch_scores.items()loop rebindsscoreto the accumulated total for each hotkey. Inside the loop,min(score, limit * score)wherescoreis already the accumulated total — not the per-job score — evaluates tomin(total, limit * total). For anylimit >= 1this always equalstotal, so the cap is never applied.Impact
DYNAMIC_SCORE_ORGANIC_JOBS_LIMITis entirely ignored wheneverlimit >= 0. Miners can accumulate organic-job scores without bound regardless of the configured cap.Fix
A fix with 12 unit tests (including a regression test) is available in PR #[to be linked].