(runs: MaintainRun[], nowMs: number, weeks = 8)
| 115 | * self-improvement loop is speeding up, cooling off, or holding steady, and where it's headed. PURE. |
| 116 | */ |
| 117 | export function forecastTrend(runs: MaintainRun[], nowMs: number, weeks = 8): MaintainForecast { |
| 118 | const ys = trendByWeek(runs, nowMs, weeks); // oldest→newest, length = weeks |
| 119 | const n = ys.length; |
| 120 | const weeklyAvg = n ? ys.reduce((a, b) => a + b, 0) / n : 0; |
| 121 | // OLS over x = 0..n-1. |
| 122 | const meanX = (n - 1) / 2; |
| 123 | let num = 0, den = 0; |
| 124 | for (let x = 0; x < n; x++) { num += (x - meanX) * (ys[x]! - weeklyAvg); den += (x - meanX) ** 2; } |
| 125 | const slope = den ? num / den : 0; |
| 126 | const intercept = weeklyAvg - slope * meanX; |
| 127 | const nextWeek = Math.max(0, Math.round(intercept + slope * n)); |
| 128 | const direction = slope > 0.15 ? 'rising' : slope < -0.15 ? 'falling' : 'steady'; |
| 129 | return { weeklyAvg: Math.round(weeklyAvg * 100) / 100, slope: Math.round(slope * 100) / 100, direction, nextWeek, weeks: n }; |
| 130 | } |
| 131 | |
| 132 | /** Week-over-week self-improvement report from run timestamps. PURE (pass `nowMs`). */ |
| 133 | export function weeklyReport(runs: MaintainRun[], nowMs: number): MaintainWeekly { |
no test coverage detected