Two τ Changes Today, and the Bug I Caught On My Own Data
Why your τ tile might look a little different from this morning — and why the change is mostly the math finally admitting it can't fit a 30-hour cycle onto a 24-hour clock face.
Most apps that change the number on your home screen do it quietly. A line in the changelog, maybe. A small banner if the team is feeling generous. The new number replaces the old number, you maybe notice it's different, you maybe don't — and the app continues using the same confident voice it used yesterday, just with a different number underneath it now. Same calm certainty, fresh value, no acknowledgment that anything has moved.
That is specifically the move I am not making today. Your τ tile might look a little different from this morning, and you deserve the long version of why.
Update — May 29, 2026: the cascade fix, and the end of the falsely confident number
Two days after I posted this, a third change shipped, and I'm not going to let it slip by in a changelog any more than I let the first two. If anything this one matters more, because it's the change that decides whether the app is ever allowed to hand you a number it can't actually stand behind.
It started, again, with a bug I caught on my own data — which is becoming a pattern I have feelings about. My May 20 night was being flagged as a 73-hour crash. It was nothing of the sort; it was an ordinary 26-hour cycle, one of my cleaner nights all month. What had happened is that the two nights before it were excluded, and the math was measuring the gap from the last surviving night — vaulting clean over the excluded ones — so my good night inherited a vast fictional gap that belonged to the days around it, not to it. The practical cost: excluding two bad nights could silently knock out a third, perfectly good one standing right behind them. The fix is the same instinct as the un-wrap, just pointed at a different problem — an excluded night still holds its place on the timeline (it happened; the clock doesn't forget it), but the gap is now always measured between time-adjacent nights and never bridged across an exclusion. One bad night can no longer poison its neighbours.
The part I actually care about, though, is what the app does when it can't give you a confident number. The old answer was to show you a single number anyway — the same calm, confident value whether the data behind it was rock-solid or barely there. And the more I sat with that, the more it felt like a small betrayal of exactly the people who'd earned the most: a tight number you can't actually trust is, in its own way, worse than an honest "not sure yet," because it hides how little it knows. So that's the thing that changed. Every result now arrives as one of four honest shapes:
- an established number, when your data supports a tight one;
- a preliminary number with a deliberately wide band and a keep logging — when there's a real estimate, it's just early;
- a "your rhythm is variable" view that hands you your own actogram instead of a single number — for the few of you whose biology genuinely doesn't settle onto one period, where a single number would be a lie dressed as precision;
- and, only for brand-new accounts with almost nothing logged yet, a "log a few more nights" — which is a starting line, not a verdict.
The point of all four is the same: the app tells you the truth about what it knows, at the resolution it actually knows it. Nobody who has been showing up gets handed a blank.
A couple of smaller things ride along. If you sleep in several pieces across a day rather than one block a night, the math now gathers those pieces into one cycle before it measures anything, so your τ stops being an artifact of the app mistaking your second sleep for a whole separate day. And if your nights swing hard from one to the next, your tile now says so out loud — ≈ X hours, varies ±Y — and tells you how many of your longest cycles it set aside as probably-skipped rather than real. That last number is a quiet admission of something I still haven't fully solved: telling a true 40-hour cycle apart from an ordinary night with a missed log is genuinely hard, I currently err on the side of leaving them out, and for the long-day drifters among us that almost certainly reads us a little shorter than we really are. I'd rather show you the count of what I'm setting aside than pretend the question is closed.
This is the third change in four days. I would like it very much to be the last one for a while — not because the work is finished, but because the math is finally doing the thing I always meant it to do, and the rest of what's left is honest enough to wait its turn. If your number moved today, it should now be the one that stays.
Mine moved by less than half an hour. Most of yours moved by even less, or not at all. A handful of you moved by an hour or two — almost always upward. Two of you got a number that came with a notably wider confidence band than before, and a personal note from me earlier today explaining why; your data is sparse enough right now that the new math has to be honest about what it doesn't yet know, and the tile leads with the range instead of the point estimate to make that visible.
Underneath all of that is one substantive change to the math and one embarrassing wiring fix that should have shipped weeks ago. The substantive one is the only one most of you will actually notice, so I'll walk through it first.
The math stopped pretending your long cycles fit on a clock face
You may remember the fork in the road from the last post — the −9.95h drift on my May 26 night that was also, on the same logged data, a +14.05h drift, depending which way around the clock face you chose to measure. The old math had to pick one or the other and genuinely could not, so it benched the night entirely. That fork was the headline example, but the underlying problem ran through the whole estimator: the old math forced every cycle's drift onto a clock face that only ran from −12 to +12 hours, and any time your real cycle exceeded that window, the math could not see it for what it was.
A 36-hour cycle on the old scale would get recorded as if it had been a 12-hour slide backward, because that's the closest thing the clock face could hold. The 36 disappeared. For someone whose biology runs at 24h, this never matters; for someone whose biology routinely produces 30, 36, 40+ hour cycles between sleeps — which my body does, and several of yours does too — the clock face was quietly losing information all month long.
The new math does not use a clock face. It measures the actual elapsed hours between your bedtimes and subtracts 24. A 28-hour cycle drifted +4. A 36-hour cycle drifted +12. A 22-hour cycle drifted −2. That is the entire change. (Yes, that's it. Yes, it really is that small a change. I'm going to come back to this in the math section at the end, because the realization that the new estimator is mostly subtraction is one of the bigger gifts this whole project has handed me, and I want to share it.)
Two other small pieces ride along with the un-wrap. The first is what I called the τ-relative gate in the threshold post — the cutoff that decides whether a long gap between bedtimes is a real long cycle or a sleepless event with an entire skipped cycle hidden inside it. The old version drew that line at a fixed 30 hours; the new version scales it to your own τ, so long-day folks get more headroom and short-day folks get the standard treatment. The second piece is that the new math respects your manual ✓ Include toggle on a sleep entry the same way every other part of the app already did — if you've ever told the app "no, count this long cycle, I know what happened that night," that override now actually reaches the τ calculation. (More on this in a minute, because there is a slightly embarrassing reason this piece is in the patch notes at all.)
What you'll see in your tile
It depends on your data, but the shape across the cohort is roughly:
- About half of you: no visible change, or a sub-half-hour wiggle. The new math agrees with the old math when your real cycles sit comfortably inside the old [−12, +12] window — which is most people most of the time. Nothing to do, nothing to notice.
- A handful of you: a real shift of one to three hours, almost always upward. This is the new math recovering biology the old math was quietly losing. You can sanity-check it against your own actogram — if your bedtimes are visibly slipping later each day by roughly the new τ minus 24, the new number is reading you correctly.
- Two of you specifically: a large shift paired with a much wider confidence band than before. Your data is sparse enough, and you'd used the ✓ Include toggle aggressively enough on unusual transitions, that the new math is now respecting all of those overrides and the resulting point estimate is being pulled hard by a handful of user-validated outliers. You got a personal note from me with the actual numbers, and your tile now leads with the band — τ probably sits somewhere in this range — rather than with a tight point estimate the math doesn't yet have the data to support. A period reported without its confidence is not a measurement, as I keep saying; for the two of you in particular, the band is the measurement right now.
And if your τ moved, the τ-based forecast on the Predict tab moved with it — the math is consistent end-to-end on that pipeline; I did not shift one number and leave another part of the app reading from a stale value. The Adaptive V2 forecast — the particle filter Jon built and wrote up properly here — runs independently of τ, doesn't depend on whether the estimator wraps your long cycles or not, and is unaffected by today's change. (It's also, for most situations, the better predictor: a swarm of sixty-four small competing hypotheses handles weird nights more gracefully than any single τ-based projection can. If you've got V2 turned on, what you saw before this morning is what you'll see tonight.)
About that bug, briefly
There is a slightly embarrassing footnote to all of this, because I almost shipped the un-wrap with a bug in it. The new estimator was supposed to respect your manual ✓ Include toggle at every layer of the math — that was literally one of the design goals of the new version — and the first cut of the code remembered the override at one layer and silently dropped it at the next. My own number was the first place this showed up: I had a 38-hour cycle from earlier this month that I'd manually marked as a real cycle, and the new math was rejecting it without telling anyone. The number on my own row of the dry-run came out around 27.90 when I knew, from living in this body, that I was sitting closer to 28. It wasn't off by much. It was off in the wrong direction — the new math was supposed to recover long cycles the old math was undercounting, not silently drop more of them — and that direction is what tipped me off.
I caught it because I'm one of my own users. The tests passed. The build was green. The cohort numbers all looked reasonable. None of that catches the kind of bug that produces a plausible-but-wrong number instead of a broken one; those bugs sit there looking believable until a person with skin in the game notices that the number isn't quite right. The fix itself was one line. Making sure two other users — whose data was about to move in surprising ways once the override carve-out was fixed — got honest, individually-calibrated notes from me before their numbers moved, was the part that actually took the work. The math change was the easy part. The wrapper around the math is always the hard part.
(If you were one of the seven people who got a direct note from me earlier today, that's why. If you got the note saying "your band is wide and the point estimate alone isn't actionable" rather than "your real τ is now X" — especially that. The math is doing its honest best with the data you've given it so far; the language around it has to match.)
The smaller fix, for completeness
There was a second change that shipped today, and I'll mention it briefly because it affects a handful of you specifically. A few weeks ago I added a setting called post-sleepless threshold — the one that controls how long a gap between bedtimes has to run before the app calls it a sleepless event and excludes it from your clean drift average. (I wrote a whole post about that one too, and how mine sits at 34h instead of the default 30, because my biological day runs long enough to bump 30 on a regular Tuesday.) The setting was visible in your Settings. The value was being saved. It was being respected by the disruption-counting machinery. And — I am still, days later, slightly horrified by this — it was not being read by the τ estimator itself. Anyone who had customized that setting was seeing a τ computed against the default 30, no matter what value they had carefully set.
That is now fixed. If you customized the setting, your long real cycles count toward your τ the way they were supposed to all along. If you left it at the default, nothing changes.
Update, May 29 2026: the cascade-fix estimator that shipped two days later changed how this cutoff is drawn. It's now *τ-relative* — max(34h, 1.3 × τ) — computed automatically from your own period rather than read from this Settings value, so the dial no longer drives your headline τ directly (it still feeds the drift display). The thing it was protecting hasn't gone away, though: if a specific long night really was one cycle, tap ✓ Include on that entry and it counts, gate or no gate. I'm reworking what that Settings control should say now that the cutoff scales itself.
Why neither of these is the math suddenly getting things wrong
When your τ moves visibly, the reasonable next thought is was the old number wrong? I've been cautious about big shifts for exactly this reason — every change costs trust, and trust is the only thing that justifies asking you to log every morning forever.
The honest answer is the same sentence I put directly into the notes I sent earlier today, because I wanted it on the record:
Both versions are honest given what data the app has on you. The new one just exposes the uncertainty more openly.
The pre-un-wrap math was doing exactly what the math underneath could see. It just couldn't see your long cycles for what they were, because the clock-face representation was lossy at the edges. The new math has more vision; the old math was not lying, it was bounded. The xDriftManual carve-out — the bug I caught — is the one strict exception to that framing: the new estimator was supposed to honor your manual overrides and didn't, and I almost shipped it that way. That one was a bug in the textbook sense. The rest is the math seeing more.
The math, in plain language
I want to walk you through what the new estimator actually does, because I think the math is genuinely understandable, and because — I have to be honest with you here — I had been mentally filing all of this under "the part where the smart-people math lives." Circadia's more sophisticated τ estimator (SMART) runs a weighted regression underneath, and weighted regression is a substantial piece of mathematical work. Is it calculus? I had genuinely wondered, sitting down to look at what would change today. The answer turned out to be: not the part that's changing. The weighted regression stayed exactly where it was. The un-wrap fix that ships today doesn't go anywhere near it. The substance of today's update is mostly subtraction, with a few comparisons against thresholds and one exponential decay function I'll get to in a minute — and the realization that I could ship a real improvement to the math without touching the regression at all was one of the bigger gifts this project has handed me.
Behavioral τ is the average length of one of your sleep-wake cycles. A cycle is one sleep plus the awake stretch that follows it. If yesterday you went to bed at 10pm and today you went to bed at 2am, that cycle was 28 hours long. Your τ is the (weighted) average of all your cycles.
Drift is just how much each cycle exceeds 24 hours. A 28-hour cycle has drift of +4 hours. A 22-hour cycle has drift of −2 hours. drift = gap − 24. It is literally subtraction. The number you see on your homepage labeled "avg drift" is the (weighted) average of all those per-cycle drifts. That's the whole story.
The old wrap problem. The previous estimator didn't use drift = gap − 24 directly. It used drift = (today's clock time of bedtime) − (yesterday's clock time of bedtime), wrapped onto a clock face. Why? Honestly, mostly because that is how this kind of math has historically been written when the assumption is that your cycle is "close to 24." For people whose cycles are close to 24, both formulas give the same answer. For people whose cycles regularly run past 36 hours, the clock-face version stops working, because the clock face wraps around and the math can no longer tell a real long cycle from a wrapped short one. The new estimator just uses drift = gap − 24. No wrap. No ambiguity. Subtraction.
The τ-relative gate. Not every gap should count toward your τ. A 50-hour gap between sleeps probably contains an entire skipped cycle inside it; it isn't one biological day, it's an awake-marathon plus a recovery sleep. So the math has to decide where to draw the line. The old version drew it at a fixed 30 hours, which works for τ near 24 and throws out real cycles for anyone whose τ is above 26. The new version scales: it draws the line at 1.3 times your own τ (or 34 hours, whichever is larger). If your τ is 28, your line is at 36.4 hours. If your τ is 25, your line is at 34. Long-day folks get more headroom; short-day folks get the standard treatment. The gate is proportional to you, not a one-size cutoff.
The high-τ damper. There is one subtlety. For users whose τ comes out really high on a first pass — over 28 — the math tightens the multiplier from 1.3 down to 1.2. This prevents a feedback loop where a high τ widens the gate, which lets in more long cycles, which pushes the τ higher, which widens the gate further. It's a damper. For everyone with τ under 28 — which is the vast majority of you — the damper never engages and you do not see it.
The xDriftManual carve-out, the bug I caught. Every sleep entry has a toggle: ✓ Include or ✗ Exclude. When you tap it, you are telling the app I know what this was, count it (or don't count it) the way I said. The new estimator was supposed to respect that toggle at the gate layer — if you said "include this 38-hour cycle," it shouldn't be thrown out as a sleepless crash regardless of what the gate would otherwise do. The first version of the code respected the toggle at one layer and forgot it at another. The fix added one line. Now it doesn't forget.
That is the entire substantive math. Drift is subtraction. The gate is a multiplier. The carve-out is an if-statement. This is not calculus. I cannot tell you how strongly I want this to land. There is no integral, no derivative, no limit, no series — not in any of the pieces I just walked through. The hardest piece is the weighting of how recent cycles count more than old ones, with a smooth fade, and that fade uses an exponential function, which is high school algebra in a fancier costume. I had been intimidated for weeks by what I assumed would require the same regression machinery the SMART estimator runs on underneath, and the answer turned out to be: it does not. The change is on a different layer entirely.
The math, technically
For the people who actually want the formulas — and to keep me honest, because writing them down is how I learned them:
Drift, per cycle:
The τ-relative gate (which cycles get included):
xDriftManual carve-out: if `entry.xDriftManual === true && entry.xDrift === false`, neither gate fires for that cycle. The user's call beats the algorithm.
EWMA-weighted mean drift — the "smooth fade" mentioned above, and the only place an exponential function appears:
Final τ:
Confidence band (σ_τ — the uncertainty that travels with the point estimate):
That is the entire math behind your τ. The substantive change today lives in a layer of the estimator I could have looked at any time, and I had been treating as off-limits because the other layer — the weighted regression underneath SMART — is the part that genuinely earns the "smart-people math" label. The un-wrap fix isn't on that layer. It is mostly the math you do at a grocery store with extra subscripts.
I am a little annoyed I let it intimidate me for so long. I am also fairly delighted that it did not have to.
The short version, for the scrollers
- The τ math just stopped wrapping your long cycles onto a 24-hour clock face. For most of you this changes nothing visible. For a handful, your τ moved upward by 1–3h — real biology the old math was quietly losing.
- A second smaller fix shipped at the same time: the post-sleepless threshold setting now actually reaches the τ math, instead of being silently ignored.
- I almost shipped the bigger change with a bug in it, and caught it on my own row because my own number wasn't quite right. The fix was one line. Telling the affected users honestly was the part that took the actual work.
- Two users specifically saw their τ shift more dramatically along with a much wider confidence band. Their tiles now lead with the band, not the point estimate. A period reported without its confidence is not a measurement.
- Neither change is a backslide. The old math was doing its honest best with what it could see; the new math just sees more.
- The math itself is not calculus, despite my deeply-held weeks-long assumption otherwise. It is mostly subtraction, with one exponential decay function for fading old data. I have been intimidated by a grocery-store calculation in a costume.
- A cohort-level write-up of how the new estimator changes the population distribution will land in a few weeks. This post is the change post; that one will be the result post.
If something looks wrong, the admin inbox is at the top of the app, or reply to whatever note I sent you earlier today and it will come right back to me.
— Dayah
FAQ
- What is τ (tau) in Circadia?
- τ is your behavioural circadian period — the average length of one of your sleep-wake cycles, where a cycle is one sleep plus the awake stretch that follows it. A τ of 24 means a 24-hour day; a τ of 28 means your body runs an extra four hours longer than the clock on the wall, every day. It's computed as 24 + your average drift, and drift is just how far each cycle runs over (or under) 24 hours.
- Why did my τ or drift number change?
- Between May 27 and 29, 2026, three connected fixes shipped: the math stopped wrapping long cycles onto a 24-hour clock face, your personal long-cycle threshold started actually being used, a manual-override bug was fixed, and excluded nights stopped inflating the gaps around them. For most people the net change is small or nothing; for long-cycle drifters it's usually a small move upward, because the old math had been quietly losing the long cycles.
- Is a higher or lower τ better?
- Neither. τ isn't a score — it's a description of your biology. A τ of 24.2 isn't "better" than 27; it's just a different body. The only thing the number is trying to be is accurate, so the forecasts built on it actually match your nights.
- What does N24 mean?
- N24 — non-24-hour sleep-wake rhythm — is a circadian rhythm disorder where your internal day isn't 24 hours, so your sleep timing drifts later (or, more rarely, earlier) a little more each day instead of staying anchored to the clock. A τ meaningfully above 24 is what that looks like in the data. Circadia exists largely because most sleep tools quietly assume everyone is a 24-hour person.
- Why does my number show a range instead of a single value?
- Because for your data right now, a single number would claim more certainty than the math actually has. The range is the honest measurement — a period reported without its confidence is not a measurement. As you log more, and as your nights get more consistent, the range tightens.
- What if I sleep more than once a day?
- If you sleep in several pieces across a day rather than one block at night, the math now groups those pieces into a single cycle before it measures your drift — so your τ reflects your real day length instead of mistaking a second sleep for a whole extra day.
- Why don't my long 35–40 hour nights count toward my drift?
- Because a 40-hour gap between sleeps is genuinely ambiguous: it might be one real long cycle, or an ordinary cycle with a missed log hidden inside it. Right now the app errs on the side of setting those aside, which means for long-day drifters it can read a little shorter than reality. Your tile now tells you how many it set aside, and a better way to tell a true long cycle from a skipped one is the next thing I'm working on.
- Did my number being different mean the old one was wrong, or the app is broken?
- No. Each version of the math was honest about what it could see at the time — the older versions were bounded, not lying. The new math simply sees more. The one true exception was the manual-override bug, which I caught on my own data and fixed in one line.
- Will my number keep changing every few days?
- That's not the plan. These three rounds landed close together because they were one connected piece of work; with them shipped, your number should settle. When the math does change again, it'll come with a post like this one — never a silent swap.
Comments