Privacy-Period-Tracker/domain
null 45aefbbcb6 fix: stop charging a drifting cycle for being followed correctly
A user whose cycle lengthens steadily was tracked to within 1.2 days and
told the app was losing confidence: her window grew from 3.1 to 5.1 days
and read Low 31% of the time, the longer it followed her correctly.

The spread estimate measured every interval's distance from a single
static centre. Under a real trend those distances grow with the length of
the history however well the trend is being followed, so the engine was
reading its own success as her variability. Residuals are now measured
against the line the centre was built on. The slope is zero unless a
trend actually fires, so every history without a drift in it is
arithmetically the previous computation -- which is what protects the §51
fixtures rather than a promise to be careful.

Two things fell out of that, and both are the same discovery.

Detrending alone took drift coverage from 96% to 77%: honest residuals
around a line cannot see that the engine deliberately under-follows the
slope, so its centre lags by design. A scale floor applies while
extrapolating a trend -- following one is still an extrapolation.

And the damping itself was paying for the measurement error. While spread
came from a static centre, following a trend inflated the number that
decided how uncertain the forecast was, so the engine had to under-follow
to stay honest. With residuals read against the line that tax is gone:
raising damping 0.50 -> 0.70 halves the error on both §51 drift fixtures
(2 days -> 1), takes fleet MAE from 0.67 to 0.44, returns window coverage
to 9/9, and *narrows* the mean window from 4.56 to 4.33.

Everything improved at once, which is the signature of removing a
distortion rather than trading one fault for another.

Measured at 400 seeds, drifting user at ten or more cycles: MAE 1.19 ->
0.97, window 5.06 -> 3.31 days, coverage 96.1% -> 94.2%, High 6.6% ->
68.1%, Low 31% -> 0.2%. The guard now pins the narrower window together
with a coverage floor, because a tighter window that stopped holding the
answer would be the trade this must not make.

closes #53

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:27:14 -05:00
..
cycle feat: guard §45's logging rules, and stop the leak that needed no log call 2026-08-18 22:01:33 -05:00
prediction fix: stop charging a drifting cycle for being followed correctly 2026-08-20 16:27:14 -05:00