Document // Engine lab
How accurate is the adaptive estimate?
calibration.json · schema v2 · seed 20261103
The Track tab's adaptive engine turns daily weigh-ins and logged calories into an estimate of expenditure as logged: the calories per day that make what you log match your weight trend. It was tested on 5,000 synthetic people from a simulator that is deliberately messier than the model: their expenditure adapts as they diet, their water weight swings for days at a time, and they under-log, skip days and mistype weigh-ins. None of it follows the filter's assumptions. By day 56 its median error is 68 kcal/day against the formula's 189, and its 80% band holds the true value for 81% of people.
- Median error, day 56, kcal
- 68
- formula 189 · 64% less
- Inside the 80% band, day 56
- 81%
- 50/80/95: 52/81/94
- Posterior SD, day 56, kcal
- 105
- 286 at day 7
- Within 150 kcal by day 56
- 85%
- median from day 35
Accuracy over time
Median absolute error by day across the realistic cohort. The engine starts from its prior, the formula lowered 10% for typical under-logging, and learns from there. It pulls below the prior for good from day 20. By day 28 its error is 109 kcal against the prior's 121 and the formula's 185; by day 56, 68 against 113 and 189.
| Day | Engine | Formula | Prior |
|---|---|---|---|
| 7 | 125 | 185 | 131 |
| 14 | 132 | 184 | 127 |
| 28 | 109 | 185 | 121 |
| 56 | 68 | 189 | 113 |
Every day (57 rows)
| Day | Engine | Formula | Prior |
|---|---|---|---|
| 0 | 141 | 185 | 141 |
| 1 | 138 | 185 | 140 |
| 2 | 132 | 184 | 139 |
| 3 | 128 | 184 | 137 |
| 4 | 127 | 184 | 136 |
| 5 | 125 | 185 | 134 |
| 6 | 124 | 185 | 133 |
| 7 | 125 | 185 | 131 |
| 8 | 128 | 185 | 131 |
| 9 | 129 | 184 | 130 |
| 10 | 130 | 184 | 129 |
| 11 | 130 | 184 | 128 |
| 12 | 131 | 184 | 128 |
| 13 | 132 | 184 | 128 |
| 14 | 132 | 184 | 127 |
| 15 | 131 | 184 | 127 |
| 16 | 130 | 184 | 126 |
| 17 | 129 | 184 | 125 |
| 18 | 128 | 183 | 125 |
| 19 | 126 | 184 | 124 |
| 20 | 123 | 184 | 124 |
| 21 | 122 | 184 | 124 |
| 22 | 120 | 184 | 124 |
| 23 | 117 | 184 | 123 |
| 24 | 114 | 185 | 123 |
| 25 | 114 | 185 | 122 |
| 26 | 112 | 184 | 122 |
| 27 | 112 | 185 | 122 |
| 28 | 109 | 185 | 121 |
| 29 | 107 | 185 | 120 |
| 30 | 105 | 184 | 121 |
| 31 | 102 | 185 | 120 |
| 32 | 100 | 185 | 120 |
| 33 | 99 | 185 | 120 |
| 34 | 97 | 185 | 119 |
| 35 | 97 | 186 | 119 |
| 36 | 92 | 186 | 119 |
| 37 | 90 | 187 | 119 |
| 38 | 88 | 187 | 119 |
| 39 | 87 | 187 | 119 |
| 40 | 87 | 186 | 118 |
| 41 | 85 | 187 | 118 |
| 42 | 84 | 186 | 118 |
| 43 | 82 | 187 | 117 |
| 44 | 80 | 187 | 117 |
| 45 | 80 | 187 | 116 |
| 46 | 78 | 187 | 116 |
| 47 | 76 | 187 | 115 |
| 48 | 75 | 187 | 115 |
| 49 | 74 | 187 | 115 |
| 50 | 72 | 188 | 115 |
| 51 | 72 | 187 | 115 |
| 52 | 73 | 188 | 114 |
| 53 | 71 | 189 | 114 |
| 54 | 70 | 189 | 113 |
| 55 | 69 | 189 | 113 |
| 56 | 68 | 189 | 113 |
Calibration
A band is honest when it holds the true value as often as it claims. By day 56 the 50/80/95% bands hold 52/81/94%. Early on they hold more, because they are still mostly the prior's: at day 7 the prior alone would hold 69/94/100%. For people who log accurately the bands are on target once data arrive: 51/81/96% at day 28 and 49/82/95% at day 56.
| Day | 50% band | 80% band | 95% band |
|---|---|---|---|
| 7 | 69 +19 | 94 +14 | 99 +4 |
| 14 | 60 +10 | 89 +9 | 97 +2 |
| 28 | 53 +3 | 82 +2 | 95 0 |
| 56 | 52 +2 | 81 +1 | 94 −1 |
Robustness to under-logging
The prior assumes people under-log by 10% on average. To see what happens when they log better or worse, the sweep reruns 2,000 realistic people at each mean logging bias. Across every level and day the bands are at most 2.6 points narrower than they claim (50% band, 20% bias, day 7), and at most 17.5 points wider (50% band, 10% bias, day 7). Each cohort's measured logging gap was within 0.5 points of its level.
| Bias | Band | Day 7 | Day 14 | Day 28 | Day 56 |
|---|---|---|---|---|---|
| 0%gatedmeasured gap 0.5% | 50% | 57 | 54 | 52 | 53 |
| 80% | 88 | 85 | 81 | 82 | |
| 95% | 98 | 96 | 95 | 95 | |
| 5%gatedmeasured gap 5.0% | 50% | 66 | 61 | 52 | 53 |
| 80% | 93 | 88 | 83 | 82 | |
| 95% | 99 | 97 | 95 | 95 | |
| 10%gatedmeasured gap 10.1% | 50% | 68 | 60 | 52 | 52 |
| 80% | 94 | 89 | 83 | 80 | |
| 95% | 99 | 97 | 95 | 94 | |
| 15%gatedmeasured gap 14.8% | 50% | 62 | 61 | 56 | 53 |
| 80% | 91 | 88 | 83 | 81 | |
| 95% | 99 | 97 | 95 | 94 | |
| 20%not gatedmeasured gap 20.1% | 50% | 47 | 51 | 52 | 49 |
| 80% | 80 | 81 | 82 | 79 | |
| 95% | 96 | 96 | 95 | 93 |
- more than 6 points too narrow (past the gate's floor)
- 3–6 points too narrow
- within 3 points of nominal
- 3–12 points too wide
- more than 12 points too wide
GatedCI asserts mean bias from 0 to 15%: at days 7 and 14 no band may fall more than 6 points under nominal. At day 56 every level, 20% included, must sit within 6 points under and 12 over. Other days are reported, not gated.
Limit20% mean under-logging is the limit. At 20% the early bands sit at the edge, 47/80/96% at day 7, and nothing asserts them. Above 20% they will be too narrow until the weigh-ins catch up.
Why this prior: the same people were also run under a prior centred on the raw formula (widen only). Its worst distance from nominal was 33.5 points, against 17.5 for the prior that ships, lowered 10% from the formula (widen and recentre).
Edge cohorts
The same engine on people the realistic cohort under-represents. Errors are median absolute errors in kcal per day; coverage is the share inside the 50/80/95% bands.
Accurate loggers
1,000 peopleThe realistic population, logging exactly what they eat.
- Error, day 56
- 72 kcalformula 113
- Coverage, day 56
- 49/82/95of 50/80/95%
- Within 150 kcal
- 86%median from day 35
The prior assumes some under-logging, so for them it starts 213 kcal low on average. The engine only beats the raw formula from day 30. By day 56 its error is 72 kcal against 113.
Missing weeks
1,000 peopleAs realistic, with days 14–27 missing: no weigh-ins and no calories.
- Error, day 56
- 70 kcalformula 182
- Coverage, day 56
- 56/84/96of 50/80/95%
- Within 150 kcal
- 84%median from day 38
With nothing to learn from, its error goes from 124 kcal on day 13 to 130 on day 27. It then falls to 70 by day 56, against 182 for the formula.
Maintainers
1,000 peopleEat at their true expenditure, so tissue weight stays flat. They still under-log.
- Error, day 56
- 46 kcalformula 186
- Coverage, day 56
- 70/93/98of 50/80/95%
- Within 150 kcal
- 94%median from day 29
Their expenditure does not adapt, so the drift the engine allows for dieters leaves their bands wide: 70/93/98% at day 56 against 50/80/95. The error still falls to 46 kcal, against 186 for the formula.
No logs
1,000 peopleWeigh-ins only. No calories are ever logged.
- Error, day 56
- 119 kcalformula 190
- Coverage, day 56
- 78/97/100of 50/80/95%
- Within 150 kcal
- 60%median from day 17
Weight change alone cannot separate intake from expenditure, so the estimate stays exactly on the prior: its error matches the prior's at every checkpoint (119 kcal at day 56).
Known limits
- It estimates expenditure as logged, not true TDEE. A scale and a food log cannot tell a steady under-logging habit from a lower expenditure, because the two only ever show up as their sum, so the engine never estimates the bias itself.
- The prior is lowered 10% for typical under-logging, which costs people who log honestly: for them it starts about 213 kcal low, and the engine only beats the raw formula from day 30.
- The gate's margins are thin. Its tightest check on this run is that the realistic cohort's 80% band at day 7 may sit at most 3 points further from nominal than the prior alone; it passes by 3.3 points. Sampling noise on a coverage share has a standard deviation of up to 0.7 points at this cohort size, and CI runs a smaller cohort on another seed, so a change that costs a few points of early coverage will fail it.
- The gate covers mean under-logging of 0–15%. At 20% the early bands sit at the edge (47/80/96% at day 7), and beyond it they will be too narrow until the weigh-ins catch up.
- A day without logged calories is filled in from the 14-day average of what was logged. Skipping the days you overeat hides those calories: your weight rises more than the log explains, so the estimate reads low, by about the skipped days' surplus over your usual intake, spread over every day, much as under-logging does.
Reproduce it
Every number on this page is read at build time from calibration.json, which one command regenerates byte for byte.
npm ci
npm run calibrate
npm test- Seed
- 20261103
- Cohorts
- realistic 5,000; accurate loggers 1,000; missing weeks 1,000; maintainers 1,000; no logs 1,000
- Bias sweep
- 2,000 people at each of 0%, 5%, 10%, 15% and 20% mean bias, the same people under each of the 2 priors
- Horizon
- 56 days, checkpoints at days 7, 14, 28 and 56
- Prior
- mean formula × 0.90, SD 14.1% of the formula
- Schema
- version 2
Engine parameters
| Parameter | Value |
|---|---|
| kcalPerKg | 7700 |
| trendSdKg | 0.02 |
| expenditureDriftKcal | 12 |
| waterPersistence | 0.85 |
| waterSdKg | 0.45 |
| scaleSdKg | 0.35 |
| missingIntakeSdKcal | 800 |
| intakeMemoryDays | 14 |
| outlierGateSd | 3 |
| initialWeightSdKg | 10 |
| plausibleWeightKg | 25–350 |
| plausibleIntakeKcal | 0–12000 |
CI runs npm test on every push and pull request. The gate in calibration.test.ts reruns a smaller seeded cohort and fails the build if coverage or error regresses past its tolerances, and a second check fails if this file no longer matches the engine's parameters or schema.