Skip to content

Guard the TimePredictor pace factor against non-positive baselines - #2239

Merged
moveson merged 3 commits into
masterfrom
oveson/claude/pace-factor-hardening
Aug 24, 2026
Merged

Guard the TimePredictor pace factor against non-positive baselines#2239
moveson merged 3 commits into
masterfrom
oveson/claude/pace-factor-hardening

Conversation

@moveson

@movesonmoveson commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Implements the surviving piece of #2238 after design review (the clamp was considered and deliberately dropped — see below). Written TDD-style: the failing specs were committed first, then the fix.

measurable_pace? checked truthiness only, and 0.0 is truthy, so:

  • A zero typical completed time produced Infinity via float division, and limits' (limit * pace_factor).to_i raised FloatDomainError — an unrescued exception in SetEffortStatus, which runs in the live-entry submission path. Reproduced verbatim in the first commit's spec run.
  • A negative actual completed time (times imported before the effort's start) produced a negative pace and therefore a negative limits band, silently flagging every subsequent healthy time "bad" with nonsense reasons — pointing reviewers away from the actually-broken time.

measurable_pace? now requires positive actual and typical completed times, falling back to the existing unmeasurable-pace convention of 1, so per-segment checks flag the genuinely broken times instead.

Why no clamp

A [0.1, 10] pace-factor clamp was implemented and then removed during review. Rationale: with #2237's pool gating in place, a corrupt flag-true baseline is rare — and when it happens, the un-clamped failure mode (mass bad flags) is loud and self-announcing, which is exactly how the #2169 incident was discovered. Clamping without an observability channel would convert that rare loud failure into a rare silent one, asserting "good" precisely when the evidence is least trustworthy. Scout capture on clamp engagement was also considered and rejected as alert-fatigue noise.

Resolves#2238

Testing

  • New #data_status with a degenerate stats baseline specs (stubbed per-segment pool averages): zero completed-segment average → no raise, evaluates against the unscaled band (pre-fix: FloatDomainError: Infinity); negative average → same (pre-fix: everything "bad" via a negative band).
  • All 20 time predictor examples pass; wider suites green (interactors, intended time calculator, enrich/verify raw time row, segment container/calculator). rubocop clean.

🤖 Generated with Claude Code

movesonand others added 3 commits August 24, 2026 08:47
Reproduces the #2169 incident shape (clean subject-segment pool,
poisoned completed-segment pool) plus the latent FloatDomainError when
the completed-segment average is exactly zero.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
measurable_pace? now requires positive actual and typical completed
times: a typical completed time of exactly 0.0 was truthy, so float
division produced Infinity and limits raised FloatDomainError. The
imputed pace is also clamped to [0.1, 10] so a corrupt pooled baseline
can shrink or stretch the limits band at most tenfold instead of
collapsing it to zero width; real pace factors (roughly 0.3..3) are
never clamped.
Resolves#2238
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per review: without an observability channel, the clamp converts a
rare loud failure (mass bad flags, which is how a poisoned baseline
announces itself) into a rare silent one. The guard remains: it fixes
the FloatDomainError crash on a zero typical completed time and stops
a negative actual completed time from producing a nonsense negative
limits band.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@movesonmoveson changed the title Guard and clamp the TimePredictor pace factorGuard the TimePredictor pace factor against non-positive baselinesAug 24, 2026
@moveson
moveson merged commit baa9aa0 into masterAug 24, 2026
7 checks passed
@moveson
moveson deleted the oveson/claude/pace-factor-hardening branch August 24, 2026 15:32
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Harden TimePredictor pace_factor: positive-pace guard and clamp

1 participant

@moveson