Recency-weighted robust centre, replacing the baseline median #10

Closed
opened 2026-08-18 03:04:45 -05:00 by null · 0 comments
Owner

BaselinePredictionEngine takes a plain median of every confirmed interval, weighting a cycle from two years ago exactly as much as last month's. PRODUCT_PLAN.md §11 names an unweighted average as an acceptable prototype and an unacceptable final engine, and §12 step 3 asks for recency weighting explicitly.

What to build, in domain/prediction:

  • exponential decay over interval age, newest weight 1.0 (§12 step 3)
  • a weighted robust centre — weighted median, or a trimmed/Huber-style weighted mean (§12 step 4)
  • the decay constant tuned by test, not picked. §12: "Tune with tests rather than guessing production values."

The trap: recency weighting and outlier robustness pull against each other. Weight recent cycles hard enough and a single recent outlier becomes the forecast, which is the §51 outlier case failing in a new way. The existing acceptance tests are the guard — all twelve must still pass.

Keep BaselinePredictionEngine in the tree. It is the thing the new engine has to beat, and a comparison test between them is what turns "different" into "better".

Verify: ./gradlew :domain:prediction:test passes all §51 acceptance cases against the new engine, and a comparison test shows lower mean absolute error than BaselinePredictionEngine across the §51 fixtures.

`BaselinePredictionEngine` takes a plain median of every confirmed interval, weighting a cycle from two years ago exactly as much as last month's. PRODUCT_PLAN.md §11 names an unweighted average as an acceptable prototype and an unacceptable final engine, and §12 step 3 asks for recency weighting explicitly. What to build, in `domain/prediction`: - exponential decay over interval age, newest weight 1.0 (§12 step 3) - a weighted robust centre — weighted median, or a trimmed/Huber-style weighted mean (§12 step 4) - the decay constant **tuned by test**, not picked. §12: "Tune with tests rather than guessing production values." The trap: recency weighting and outlier robustness pull against each other. Weight recent cycles hard enough and a single recent outlier becomes the forecast, which is the §51 outlier case failing in a new way. The existing acceptance tests are the guard — all twelve must still pass. Keep `BaselinePredictionEngine` in the tree. It is the thing the new engine has to beat, and a comparison test between them is what turns "different" into "better". Verify: `./gradlew :domain:prediction:test` passes all §51 acceptance cases against the new engine, and a comparison test shows lower mean absolute error than `BaselinePredictionEngine` across the §51 fixtures.
null added this to the Batch 02 — Prediction Engine milestone 2026-08-18 03:04:45 -05:00
null closed this issue 2026-08-18 03:16:15 -05:00
Sign in to join this conversation.
No Label
P0
P1
P2
release-blocker
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: null/Privacy-Period-Tracker#10
No description provided.