Commit the learning-curve simulation as a permanent guard #50
Labels
No Label
P0
P1
P2
release-blocker
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: null/Privacy-Period-Tracker#50
Loading…
Reference in New Issue
No description provided.
Delete Branch "%!s(<nil>)"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What is true now. docs/qa/ClaudeQACoverage.md records a standing gap: whether predictions measurably improve at 3, 6 and 12 confirmed cycles cannot be reached by any QA round, so 'the product's headline claim is tested only at the unit level'. A throwaway simulation has now measured it (stable users reach a ~0.8-1.0 day MAE floor by cycle 2-3; variable users never converge and are correctly reported as uncertain), but nothing in the tree stops that regressing.
What to do. Add
LearningCurveTestto domain/prediction/src/test: four profiles (stable 28, stable 35, variable 25-37, drifting +0.5 d/cycle), 40 seeds each, 15 cycles, the loop mirroringCycleRepository.forecastincluding fed-back absolute errors. Aggregate per cycle-count across seeds and assert on ranges — MAE ceilings, coverage floors near WINDOW_MASS, confidence-label shares. Print the per-k table as EngineComparisonTest prints its own.Traps. Use
java.util.Random(seed): its algorithm is frozen by the Java spec, whereaskotlin.random.Randomis explicitly not stable across Kotlin releases and would make a committed guard flaky on a toolchain upgrade. Assert on aggregates, never a single (profile, seed, k) cell. Land the guard at thresholds the current engine already meets, then tighten each row in the calibration commit it guards, observing red-before/green-after — a guard never seen failing is not known to guard anything (docs/architecture/GUARDS.md).Verify:
./gradlew :domain:prediction:test --tests '*LearningCurveTest*'green in under two seconds, and docs/qa/ClaudeQACoverage.md's standing-gap entry updated to say the simulated-history half is closed and on-device elapsed time remains open.LearningCurveTest lands at domain/prediction/src/test/kotlin/dev/privacyllc/period/domain/prediction/LearningCurveTest.kt in 335d021: four profiles, 400 seeds each, 15 cycles, mirroring CycleRepository.forecast including fed-back errors. Runs in 1.2s. Committed at thresholds the then-current engine met, then each calibration commit tightened its own row red-before/green-after. It immediately earned itself by finding #51 and #53. Run: ./gradlew :domain:prediction:test --tests 'LearningCurveTest'