Net, and calm
Over on Lazer, under on Mercado: they cancel and the number does not move. Compensation is automatic, because overspending one category while saving another is good money management, not a failure.
A single number in the home blob, and the one decision everything else follows from: it is built over time and never resets. One bad month barely dents it. A good streak lifts it slowly.
The number is not an accounting figure. It is a reputation-like signal about financial behaviour over time, and it drives three things: the figure in the blob, the blob’s mood, and its colour.
Splitting it in two is what makes that possible. Cycle Health answers “how am I doing this period” and resets every cycle. The Score never resets — it drifts toward whatever Cycle Health reports, slowly.
There is no Open Finance integration yet. People import statements weekly or monthly, which means the last few days are almost always empty. Any “spending in the last seven days” signal would be measuring statement lag rather than behaviour, so the model uses none.
What survives batch import is dates. Transactions carry real ones, so spend-to-date against expected-by-date stays valid even when a whole month arrived in a single afternoon.
The naive check is expected = budget × daysElapsed / daysTotal, which assumes money leaks out evenly. Rent does not leak. It hits on day 1 and never again.
On day 18 the linear model claims Moradia should sit at R$1.320. Rent was paid in full on day 1, so it sits at R$2.200 — and the check screams 67% over pace at somebody who is perfectly fine, across 40% of their budget.
So expected is shaped per category. Two shapes in v1, and classifying costs nothing: the essentiality algorithm already reads frequency, recurrence and rigidity. Rigid, recurring, once a month is committed. Frequent, variable, spread out is flow.
Two terms, weighted 65/35 — process and consequence.
H = clamp( 0.65 · Pace + 0.35 · Savings, 0, 1 )
Pace asks whether you are net under the line right now. Variance is computed per category and rolled up on the net, and the first case is the one that matters.
variance = spent − expected(day) − surplus, + over netVariance = sum of every variance Case A — netVariance ≤ 0 Pace = 1 full stop, zero penalty Case B — netVariance > 0 discShare = discretionary share of the positive variance k_blend = 2.5 · discShare + 1.2 · essShare Pace = clamp( 1 − k_blend · netVariance / total budget, 0, 1 )
Savings asks where the month lands against the goal, projected mid-cycle so there is no delayed feedback. It is not Pace restated: savings is the thin gap between income and budget, so it is leveraged. In the worked example below, spending 7% over budget erases 39% of the savings goal. Pace measures process; Savings measures what that process costs. The gap between them is the information.
projectedSpend = committed budgets never extrapolated + flow spend × daysTotal/daysElapsed Savings = clamp( projectedSavings / plannedSavings, 0, 1 )
Every category is still computed in full — expected, spent, variance, essentiality. That data powers the UI and the coach. What changes is only how it rolls up into the one number.
Over on Lazer, under on Mercado: they cancel and the number does not move. Compensation is automatic, because overspending one category while saving another is good money management, not a failure.
The same event still surfaces — you went over on Lazer, but you made it up on Mercado. Awareness without punishment, framed as praise for the trade-off. It never touches the score.
target = H × 100 S_new = clamp( S_old + alpha(n) · rate · (target − S_old), 8, 100 ) alpha(n) = max( 0.08, 1 / (n + 3) ) n = cycles of evidence fed rate = clamp( newDaysCovered / daysTotal, 0, 1 )
rate is the fraction of genuinely new information, which solves the early-month problem for free: on day 2 there is almost nothing new, so the score barely moves however noisy the health reading is.
alpha(n) is the interesting one. With alpha = 1/n exactly, the score is the running average of every cycle health ever recorded — the update rule and the average are the same thing. It is not an arbitrary decay curve; it is literally your average cycle health, weighted by evidence.
Two adjustments to that ideal. A starting offset of three gives the initial 50 the weight of about three months, so one imported month cannot slam a new user to 95. And a floor of 0.08 stops the score freezing: pure 1/n reaches about 0.006 by month 180, at which point somebody who reformed could never climb back — which destroys the entire coaching value.
n counts cycles of evidence fed, not calendar age — otherwise inertia could be earned by doing nothing.
Income R$6.500, budget R$5.480, so the savings goal is R$1.020. Her persisted score is 62, and her last import covered through day 4.
Her blob ticks 62 to 63. The month is “worth” an 81, but she has history now and only half a month of new data arrived.
The payoff is the bad month. If the next cycle collapsed to a target of 20 with a full month of data, the score would fall to about 57 — a disaster costs roughly five points, not forty-two. Two good months pull it back, and by month 24 the same disaster would cost about 3.4. Hard to build, hard to lose, always recoverable.
Still open. Six questions the record leaves unanswered — whether the starting offset and the alpha floor survive contact with real scenarios, whether rate should count days or amounts, whether recovery should be slower than damage, whether 65/35 is the right balance, what to do with a committed charge that has not posted by mid-cycle, and whether evidence should decay after a long gap. They are listed in full in the record.