selfReputationThrottle is documented as a soft cadence throttle: "a recovering ratio restores it — never a hard permanent ban" (packages/loopover-engine/src/governor/reputation-throttle.ts ~5), returning cadenceFactor ∈ [minCadenceFactor, 1] (~47-54).
The chokepoint ignores that entirely:
if (reputation.throttled) return denyResult({ stage: "reputation_throttle", ... }); // chokepoint.ts ~300-311
cadenceFactor is placed in extraPayload (~309) and read by nothing — a repo-wide grep across packages/ and src/ finds no consumer.
Meanwhile incrementReputationHistory (packages/loopover-miner/lib/governor-state.ts ~415-432) maintains a cumulative, never-decaying (decided, unfavorable) counter per (api_base_url, repo_full_name) — no window, no decay, no pruning. It is reachable in production via attempt-cli.ts ~774.
The absorbing state: a miner reaches 5 decided outcomes with 3 unfavorable → ratio 0.6 ≥ throttleAtRatio: 0.5 → throttled: true. Every open_pr is then denied outright. Since submissions are the only source of new terminal outcomes, and the counter never decays, decided and unfavorable freeze forever. The ratio can never fall back below the threshold. The miner is permanently banned from that repo, with no recovery path and no operator-visible reason beyond reason: "throttled".
Three bad contributions early in a repo = permanent exclusion, contradicting the module's own documented contract.
Fix
- Consume
cadenceFactor by scaling the write-rate-limit window rather than denying; reserve a hard deny (if any) for reason === "floored".
- Give
governor_reputation_history a decay or bounded window so it cannot become absorbing.
Acceptance
- A throttled miner submits at reduced cadence and can recover its ratio through subsequent good contributions.
selfReputationThrottleis documented as a soft cadence throttle: "a recovering ratio restores it — never a hard permanent ban" (packages/loopover-engine/src/governor/reputation-throttle.ts~5), returningcadenceFactor ∈ [minCadenceFactor, 1](~47-54).The chokepoint ignores that entirely:
cadenceFactoris placed inextraPayload(~309) and read by nothing — a repo-wide grep acrosspackages/andsrc/finds no consumer.Meanwhile
incrementReputationHistory(packages/loopover-miner/lib/governor-state.ts~415-432) maintains a cumulative, never-decaying(decided, unfavorable)counter per(api_base_url, repo_full_name)— no window, no decay, no pruning. It is reachable in production viaattempt-cli.ts~774.The absorbing state: a miner reaches 5 decided outcomes with 3 unfavorable → ratio 0.6 ≥
throttleAtRatio: 0.5→throttled: true. Everyopen_pris then denied outright. Since submissions are the only source of new terminal outcomes, and the counter never decays,decidedandunfavorablefreeze forever. The ratio can never fall back below the threshold. The miner is permanently banned from that repo, with no recovery path and no operator-visible reason beyondreason: "throttled".Three bad contributions early in a repo = permanent exclusion, contradicting the module's own documented contract.
Fix
cadenceFactorby scaling the write-rate-limit window rather than denying; reserve a hard deny (if any) forreason === "floored".governor_reputation_historya decay or bounded window so it cannot become absorbing.Acceptance