Why the habit streak forgives, and what that cost us
A habit streak usually works by making people feel bad. We built Grace Tokens, Flare Mode and a Bereavement Hold instead, and week-six retention improved.
Blog Behaviour
Most engagement systems treat notification timing as a delivery problem and optimise for the message being opened. We hold roughly a third of everything the engine wants to send, and that number is on the dashboard where everyone can see it.
Notification timing is not a delivery detail. A reminder to stand up, delivered while you are driving, is not a neutral event. It is a small tax on attention and a slightly larger one on trust, and after enough of them the person turns notifications off — at which point the product is finished, however good the recommendation engine is.
So the context agent runs before every send, and returns one of three verdicts: deliver, defer with a retry window, or block with a named reason. A block is recorded as a successful decision, not a failure to deliver.
Driving, detected from motion state rather than location. An active call. Do-not-disturb. The daily cap, which is per person and per mode. A minimum gap since the last one. Quiet hours. A calendar block marked as focused, which we read structurally — the titles never leave the device.
When a send is blocked, the API returns it as a success with `held: true`, the blocking reasons, and a retry window. It is not an error, and treating it as one is how you end up with a client that retries into the same wall.
Held rate as a headline metric makes every growth review slightly uncomfortable, because the obvious lever for weekly active users is to send more. We have the number in front of us specifically so that lever is visible when somebody reaches for it.
What we measure instead is completion rate per delivered message. Sending fewer and better moves that number in the direction that actually correlates with people still using the product in month six.
Notification timing is decided by a contextual bandit with a sub-second budget. It is not an LLM and the architecture notes say so explicitly, because "upgrade it to a model" is a suggestion that arrives about twice a year and is wrong every time.
The most useful signal for when somebody can move is their calendar, and the calendar is also the single most sensitive thing on a work device. Titles contain client names, medical appointments, disciplinary meetings and resignations.
So the calendar is read structurally. Start time, end time, busy or free, accepted or tentative, number of attendees, whether it recurs. The title is never transmitted, never logged and never sent to a model — it is in the redaction list the AI Gateway enforces before any external call, alongside names, clinical notes and free-text responses.
This costs accuracy. A meeting called "1:1 with Sam" and one called "quarterly board review" look identical to us, and the second is a much worse moment to interrupt. We accept the worse recommendation, and the notification a person receives says "calendar titles never left your device" so the trade is visible rather than assumed.
The response is a success with `held: true`, an array of blocking reasons, and a retry window in seconds. Not a 4xx, not an empty body, not a silent no-op.
That shape matters more than it looks. A client that receives an error retries, logs an exception, and eventually shows the user something is broken. A client that receives an explicit hold with a retry window waits, and can tell the person why if they ask.
Completion rate per delivered message, split by mode. It is the number that moves when the timing model improves and the number that falls when somebody quietly raises a daily cap to hit a weekly target.
Held rate sits next to it, unexplained and deliberately prominent. If it drops sharply without a corresponding change to the context model, something has been loosened and somebody should have to say what.
Driving, detected from motion state rather than location. An active call. Do-not-disturb. The daily cap, which is per person and per mode. A minimum gap since the last one. Quiet hours. And a calendar block marked as focused, which is read structurally.
No. It reads start time, end time, busy or free, accepted or tentative, attendee count and whether it recurs. The title is never transmitted, never logged and never sent to a model — it is in the redaction list the AI Gateway enforces before any external call.
The API returns a success with `held: true`, an array of blocking reasons and a retry window in seconds. Not a 4xx, not an empty body, not a silent no-op — a client that receives an error retries into the same wall.
This article sits in the micro-movement cluster and links up to /micro-movement. Clusters are how the editorial agent decides what to write next — the thinnest one wins.
Reading this page records the time it was visible and how far it was scrolled. No cookie, no identifier, and the connecting address is hashed with a salt that regenerates daily. How that works.
Other pages here that point at this one. Internal links are the part of “backlinks” a site actually controls — nothing on this platform buys or exchanges the other kind.
Read next
A habit streak usually works by making people feel bad. We built Grace Tokens, Flare Mode and a Bereavement Hold instead, and week-six retention improved.
Charter rule C6 banned body composition framing at every age. Then we specified a product built on exactly those numbers. Here is the whole argument.
We moved the platform invariants into database constraints. Postgres rejected four writes the same afternoon that the services had been allowing.
Everything described here is running. An account is free, takes about two minutes, and the writing stays free whether or not you make one.
Free to create. 150 AI credits a month for 2 months, then AI features need a plan — everything that is not AI carries on either way. Premium is £5.99 a month and cancels in one click. Under 18 a guardian confirms before the account opens.