Build log
Changelog_
Kalcius ships most days. Every build that changes what you can do is logged here — features, fixes, hardening. The latest 20 entries are below; the full log lives in the archive, month by month.
The server's answers become the test paper
- This morning's new iPhone test rig had one honest asterisk: the sample server answers it tested against were copied out by hand. Careful hands, but hands — if the server's real answers ever drifted from the transcription, the tests would keep passing against yesterday's truth. As of tonight the samples are generated by the server's own code: fifteen real answers — your account, her cycle status and calendar, test results, the retest window, the two-minute check, notification settings, heat events, feedback, badges, the weight trajectory — written down exactly as the wire carries them, quirks and all (decimal values that arrive as text, dates to the millisecond).
- Three locks hold the loop shut: the server refuses to build if a stored sample stops matching what its code actually sends; the iPhone build refuses if any sample stops decoding — or if a new sample appears with no test reading it; and the website's checks read the same files. A change on any side that would break another now goes red on the build board before it can reach a phone.
- Writing it down honestly caught a small fib in the old hand-copied version: it tested the iPhone against a heat-event source label the server cannot actually send. Nothing was broken — but that is precisely the class of quiet drift this machinery exists to end.
The iPhone's engine room meets a compiler
- Until today the iPhone app had one honest gap in its safety net: the machinery that checks every update could catch a typo in the code, but not a wrong assumption — whether the app still reads the server's answers correctly, whether the doctor's summary sheet still builds the same document as the website's, whether a reference value quietly drifted. All of that was checked by a careful human read, every day, by hand. As of today the core of the app — how it reads your data off the wire, the doctor's copy, the WHO reference points, the weight-unit conversions, the shared vocabularies — is compiled and put through sixty-one real tests by machine on every single change.
- The very first pass caught something real: if the server ever sent an impossible calendar date (a February 31st), the iPhone would have quietly rendered it as a different day — early March, with nothing to say it wasn't yours. It now refuses to show a day the server never sent, the same rule the server itself follows.
- The tests also pin the two platforms together, word for word: the reference values on a semen analysis, the wording of what's shared with a partner and what's withheld, the labels on notifications — the iPhone and the website can no longer drift apart on any of them without a build going red.
The welcome mat was never out
- Today's two truth-telling sweeps got the house treatment: read back line by line, adversarially, before anything new was built on them. The iPhone sweep held clean — every new call checked against the real API, every screen's states re-derived. The website's sweep held too, but auditing it turned up something older hiding underneath: the "New here? Run first briefing" pointer on the Today page — the one card meant to greet a brand-new account and point it at the three-minute setup — could never actually appear for the person it was written for. A quirk in how the page judged "nothing here yet" meant the only visitors who ever saw it were ones whose connection had partly failed. The welcome mat existed; it was just never put out. It now greets exactly who it should: every read landed, nothing logged anywhere, here's where to start.
- A browser test now registers a fresh account and checks the welcome card is standing, so the front door can't quietly lose its greeting again.
- And one manner the iPhone learned yesterday reached the website: on a dropped connection, a linked partner's Today page no longer quietly rearranges itself while a read is failed — the tiles stay put and say what happened, same rule on both platforms.
The iPhone stops lying about bad connections
- This morning's fix taught the website to admit when a request fails instead of dressing the failure up as an empty page. This afternoon the iPhone app got the same lesson, because it had the same disease everywhere: on a flaky connection Today could declare "All cool" over a heat log it never fetched, tell a man with months of results "Nothing logged yet — log your first test", and invite a linked partner to "set up partner tracking"; Key dates, the Playbook and the medications ledger sat on "Loading…" forever with no way back; the feedback page could quietly show your sent history as if it had never existed; and My Fertility's quieter panels — the retest window, the two-minute check, the Body charts — simply weren't there, indistinguishable from features that don't exist.
- Every screen now says what actually happened, in its place, with a Try again that re-fetches — the same honest line, word for word, as the website's. Today gets the one-line summary at the top for the panels that hide when empty, the Comms screen never claims the AI writer is off when it merely couldn't check, notification settings explain themselves instead of sitting greyed out, and a heat event you delete or re-tag that didn't save says so instead of quietly reappearing.
- The sweep also caught a proper bug hiding in the calendar: the day editor's "Your levers" section — the quick Met / Not chips for logging your daily habits from the grid — could never appear at all, because the code that fetched the lever board only ran once the section was already on screen. It loads properly now, on every day within the two-week logging window, and a lever tap that doesn't save tells you nothing was saved.
The whole shop stops lying about bad connections
- Yesterday's find in the staff room — panels that quietly vanished when a request failed — raised an obvious question: does the rest of the shop do that too? It did, all over. On a flaky connection the feedback page could show you a history with nothing in it, as if everything you'd ever sent had gone; the playbook could greet a page of logged moves with "Nothing logged yet"; the medications ledger could claim "Nothing logged" over real entries; the weight card could say "No weigh-ins yet" to someone with months of history. Worst of all, the Today page: a failed heat-exposure check declared "All cool", a failed test lookup said "Nothing logged yet — log your first test", and a failed status check told a linked account to "set up partner tracking". Every one of those is a confident statement about your data made by a page that hadn't managed to read it.
- All of it now tells the truth. When a section fails to load it says so, in its place, with a Try again that re-fetches just what's missing — no reload, no lost work. Today gets one honest line at the top for the panels that hide entirely when they're empty, so a thin-looking morning can never silently be a broken one. The Comms page no longer claims the AI writer is switched off when it simply couldn't check, and notification settings no longer sit on "Loading…" forever with no way back. Deleting or re-tagging a heat event that didn't save now says it didn't, instead of quietly putting the row back.
- And the browser tests have learned to break the network on purpose: they now cut individual connections mid-page, watch the honest message appear where the false "all clear" used to be, and click Try again to see the real data land. A page that goes back to bluffing fails the build.
The staff room gets the fresh-eyes walk
- The last two walks covered his side of the shop and hers; today's covered the room behind the counter — the admin page, where feedback gets triaged, badges and articles get their copy tended, and the aggregate numbers live. The walk caught something real in the act: partway through, two whole panels quietly failed to load and simply weren't there — no error, no gap, no way to tell the difference between "this failed" and "this doesn't exist". A reload brought them back, which is exactly the problem: a flaky moment was indistinguishable from a missing feature.
- So the whole page has learned some manners. Every panel that fails to load now says so, in its place, with a Try again button that means it — no more sections that vanish, no more failed reads dressed up as good news ("Nothing here. Good." over a feedback list that simply hadn't arrived). Moving a feedback item that doesn't save now says it didn't, instead of appearing to work. And the worst of them: if the page couldn't check who you are, it used to show the polite turned-away line meant for non-staff — an error wearing a denial's clothes. It now says the check itself failed, and offers the way back.
- The admin page also joins the browser-test rota at last — the door, every panel, a feedback triage move and the accessibility sweep are now walked by machine on every update, so a panel that goes quietly missing is a failed build, not a mystery for the next person who happens to look.
The site learns to travel light
- Today was spent weighing the website the way a first-time visitor's phone does, and the scales said two things. First: the pages never told browsers what was safe to remember, so every visit re-downloaded half a megabyte of app machinery that hadn't changed since the last one — and, worse, a browser guessing on its own could keep an out-of-date page past a deploy and render a broken site until its guess expired. Both ends are now stated properly: the parts of the site that never change once shipped are marked keep-this-for-a-year, and the pages themselves always check for a newer build before reusing what's cached. Repeat visits get faster; updates arrive the moment they ship. The watchdog that probes the live site every half hour now checks these promises from the outside, too — including that the server is compressing what it sends.
- Second: this changelog had quietly become the heaviest page on the site — it grows most days, and it was all delivered as one ever-longer scroll. It now opens with the newest twenty entries, and the full history lives in a month-by-month archive linked underneath — every entry still there, every month on its own page, nothing lost. If you've ever wanted to re-read what shipped in July, July now has an address.
The settings learn who they're talking to
- Yesterday's walk through the partner's side of the shop left one thing on the list: her settings still described his product. The notification panel promised a daily brief of "her status, the window, and today's top play" — she is the "her"; her brief has never contained any of that — offered to keep "her cycle out of your inbox", and explained that "her updates" can't be silenced. Today that's fixed, on the website and in the iPhone app both: on a linked partner's account every one of those lines now says what her account actually gets — a brief of her own wins and streaks, an email that carries no cycle detail because she doesn't track one, and the sharing announcements that really are impossible to silence. His settings read exactly as they always did, and hers switch back to the standard wording the moment she starts tracking a cycle of her own.
- One of the finds ran deeper than wording. The phone's Privacy screen has a row for the partner-data consent — the dated record created the moment a partner accepts a link. On her account that consent is hers: she created it by accepting, and ending the link is how she withdraws it. The note under it, written for his side of the app, told her it was "Hers, not yours" and pointed at a settings page her account doesn't have. It now tells the account whose consent it actually is: yours, dated from your acceptance, ended any time from Shared with you.
- A browser test now reads her settings page on a freshly linked account and fails if any line ever talks about "her" again.
Her side gets the fresh-eyes walk
- This morning's walk covered his side of the shop; this afternoon covered hers. A brand-new invite was sent, the email opened, an account created from it, and every page walked the way a partner actually meets them — the invite letter, the accept page, her first-day briefing, the shared log, her day-correction form, her settings, and the way out — on a desktop and again at phone width. The mail, the briefing and the shared log all held up, and her exit stood the link down cleanly from her side alone.
- One thing didn't hold up, and it's fixed on the website and in the iPhone app both: her Today page was talking to her as if she were him. It invited her to "set up partner tracking" — she is the tracked side; the cycle it proposed to track is her own, already one tap away — suggested she "add her birthday and your anniversary", offered a daily comms plan of things to say to "her", and kept a permanently empty fertile-window tile waiting for cycle data that lives in the shared log. Those panels now stand down on a linked partner's account. Her Today opens with the shared log — the thing the invite brought her there for — followed by the parts of the app that are genuinely hers to explore or ignore, exactly as her briefing promises. And if she ever starts using those features in her own right — tracking a cycle of her own, adding her own key dates — the panels come back, because then they're hers too.
- A browser test now walks the whole journey — invite to accept to her Today — so her first screen can never quietly turn back into his.
A walk through the shop with fresh eyes
- Today a brand-new account was created and every page of the website walked the way a first-time user would meet it — sign-up, the briefing, then all fourteen dashboard pages on a desktop and again on a phone-sized screen. Most of it held up. Three things didn't, and all three are fixed.
- First, a dead end at the exact moment the app should be leading you by the hand: set up partner tracking and the Today page says "Log the first day of her period to start predictions" — as plain text, with nothing to click. Every neighbouring tile offers a door; the most important first step in the product didn't. The sentence is now a link straight to the Partner page, where the log button waits. (The iPhone app already had this button — the website just caught up.)
- Second, the Comms page greeted ordinary users with a sentence written for the server's operator — instructions about environment variables and restarting services. If you don't run the server, that's noise wearing a warning's clothes. It now says the honest, useful thing — the AI writer isn't switched on yet, and the daily plan below still works without it — and keeps the operator's instructions for the operator's eyes only.
- Third, a claim that had quietly stopped being true: the Plus page said that until billing switches on, "nothing is locked." Not quite — the AI features are open to everyone, but charts still keep to the free tier's 7-day history window. The page (on web and iPhone both) now says exactly that, because a subscriptions pitch is the last place a fertility app should be imprecise.
The watchdog stops trusting the clock
- Kalcius has a watchdog: an automated probe, run from outside our own machines, that checks the live site, the API, the database backups and every safety check — and raises an alarm the moment anything is wrong. It was asked to run every half hour. Reading its actual history this morning showed it hadn't been: the scheduler it relies on quietly drops runs when it's busy, and over the last three days the "every 30 minutes" watch had really fired three to five times a day, with unwatched gaps of over ten hours. Nothing broke in those gaps — but nothing was watching either, and a watchdog that isn't running can't tell you it isn't running.
- So it no longer trusts the clock. The probe now fires automatically the moment anything that changes the product finishes — every deploy, every test run, every nightly backup, whether it succeeded or failed — so a broken update raises its alarm within a minute of failing instead of whenever the scheduler next felt like it, and every update gets checked right after it lands. The clock-driven sweep still exists for things that break on their own between updates. And the watchdog now takes its own pulse: every probe states how long the watch actually stood empty since the last one, so thin coverage shows up in the record as what it is, instead of passing for coverage.
The manners get their shakedown
- Midday's quiet hours got the treatment everything here gets the day it ships: read again the way an auditor would, and fixed the same day. The find: a push held overnight was delivered when your quiet window ended even if you'd already opened the app in the small hours and read the notification there. The morning buzz would announce something you saw eight hours ago. Now, anything you've already read releases silently when the window ends — the push exists to surface what you haven't seen, and only that still gets one.
- And the iPhone's settings panel learned the website's honesty: if saving a notification switch fails — bad signal, mid-tunnel — it now says so and puts the switch back the way the server actually has it, instead of leaving it showing a choice that never took.
Notifications learn manners
- You can now choose what Kalcius is allowed to interrupt you about. In Settings on the phone — and, for the first time, in a proper Notifications panel on the website's Profile page — each kind of push has its own switch: cycle alerts (Code Red, Code Orange, the fertile window), key-date reminders, health reminders (the retest window, the monthly two-minute check), and friend activity. Switch one off and that category goes fully quiet — no push, no bell badge — while everything else still arrives. One deliberate exception, stated right under the switches: partner-link announcements (linked, ended, sharing changed, her updates) have no off switch, because the link's honesty depends on them being impossible to silence.
- And quiet hours, at last: set a window — say 10pm to 7am, in your own timezone — and no push will light your lockscreen inside it. What fires overnight isn't lost, it's held: a Code Red that trips at 2am arrives when your window ends, still true and actually readable, and the in-app record is there the moment it happened either way. Your daily brief is the one exception — you picked its hour yourself, so that hour wins, even inside the window.
- The website catching up matters on its own: notification preferences — discreet mode, the daily brief and its hour, the weekly email — could previously only be changed from the iPhone app. Anyone using Kalcius purely in a browser, which is every Android user, had no way to reach them. Now both apps offer the same switches, described in the same words.
The thermometer's code meets its compiler
- Kalcius is three kinds of code: the server and website (hundreds of automated checks), the iPhone app (run through the real Swift parser since yesterday), and the firmware — the code that will live inside the Clip thermometer itself. Until today the firmware was the last layer with no machine check at all: changes shipped verified by careful reading alone. Now every change to it is fully compiled for the exact chips it runs on — the Clip's Bluetooth chip and the bench rig's — the moment it lands, by the same command-line toolchain the workshop bench uses.
- This check is actually stronger than the iPhone's: a parser only proves the code is well-formed text, but a full compile proves the types line up, the radio and sensor libraries are being called correctly, and nothing is missing — the whole program builds, ready to flash. The honest limit is stated where the check lives: it compiles the firmware, it can't run it. How the radio behaves, how the sensor reads, how long a battery lasts — those still need the physical bench.
- And like every check here, the checker is itself watched: if a firmware build ever goes red, the production watchdog raises it on the build board within the half hour.
The iPhone's code gets a proofreader
- A quirk of how Kalcius is built: the iPhone app is written in a workshop with no Apple tools in it, so until today every change to the phone's code shipped checked by careful reading alone — the website and the server have hundreds of automated checks; the phone had a proofread. Now the machinery exists: the moment any change to the iPhone code lands, the entire tree is run through the real Swift parser — the same one Apple's tools use to read the code — and a malformed file stops the line within minutes, instead of surfacing days later when the next App Store build is attempted.
- It's an honest, limited check, and the limit is stated where the check lives: it proves every file is well-formed Swift; the deeper checks still happen when the app is built for TestFlight. And it earned its keep within minutes of existing: its very first run refused to pass, because the checker was reading a slightly older dialect of Swift than the app is actually built in. That got fixed — and the exact dialect is now pinned in the project itself, so the checker and the real build can never quietly disagree — and the second run proved all 55 files of the app, today's screen-reader work included, parse clean.
- And like every other check here, the checker is itself watched: if the parse ever goes red, the production watchdog raises it on the build board within the half hour.
The iPhone learns to be read aloud
- This morning the website learned to work with a screen reader; this afternoon the iPhone app caught up with VoiceOver. Every control that spoke in emoji now speaks in words: the reaction row on the composer, the date-night log and the mood buttons announce their meaning — “Loved it”, “Frosty”, “Irritable” — not the symbol’s dictionary name. It’s the same seven-word vocabulary the website uses, defined once, so the two apps can never describe the same button differently.
- Everything that toggles now says whether it’s on. Mood and flow chips, the self-exam outcomes, the sharing-scope switches, the playbook’s rating stars, a pinned date idea — all announce their selected state instead of carrying it in a border colour VoiceOver can’t see. Glyphs that carried meaning got words too: the flow dots say “Flow 2 of 4”, a date idea’s price and effort marks say “Cost 2 of 3, effort 1 of 3”, a rated move says “Rated 4 of 5”.
- Every unlabelled button got its name: the little × that deletes a result, a key date, a play or a draft now says exactly what it removes; the + in each toolbar says what it adds; the calendar’s arrows say “Previous month” and “Next month”; the date pickers on the partner setup and shared calendar carry their captions. And the levers catalog’s “more” hint became a real button a screen reader can find — it was a bare tap zone before.
- One thing any eye can check: locked badges were dimmed to little more than half strength, the same legibility mistake the website fixed this morning. They’re now drawn hollow — dashed edge, unfilled ground — with the text at full strength, so “locked” is carried by shape, not by making the words hard to read.
The app learns to be read aloud
- Kalcius now works properly with a screen reader, and stays that way by machinery. Every form field on the site — the day editor, the test log, the key dates, the profile, all of them — now carries its label in a way assistive technology actually announces, instead of a caption that merely sits nearby. A practical win for everyone else too: clicking a field's name now focuses the field.
- The details a screen reader was guessing at now speak plainly. The emoji you tap to log her mood or rate a date night announce their meaning in words, not their symbol name; buttons that toggle — moods, productivity, self-exam outcomes, units — announce whether they're currently on; the notifications button says how many are unread; and the current page is named in the navigation. Keyboard users get a proper front door too: the first press of Tab on any dashboard page offers "Skip to content", so reaching the page never means tabbing through the whole sidebar.
- Things any eye can check got fixed as well: links inside sentences are now underlined, not distinguishable by colour alone; locked badges are drawn hollow instead of dimmed to illegibility; the faintest small print was lifted to proper contrast; and the playbook's rating stars grew to a real tap target.
- None of this relies on good intentions. The robot that walks the app before every update now runs the industry-standard accessibility audit (WCAG A and AA) on every public page and every dashboard surface — with the big forms open, not just closed — and a single violation stops the release, the same way a broken button would.
The rehearsal reaches the street door
- The day's third pass closed the robot user's last gap: the public site — the front page, the free Learn library, this changelog, the privacy page — is now walked in a real browser before every update, signed out, the way a stranger arrives. The rehearsal checks that every reviewed article in the library is actually on the shelf, opens one and reads it, follows a link inside the copy to the next article, and then takes the door into the app — confirming it lands, honestly, on the sign-in screen.
- The privacy page makes a claim most sites can't: no analytics, no cookies, no third parties. That promise is now enforced, not just stated — on every build, the robot browses the public pages and fails the update if a single request leaves for anyone else's server, if anything asks the API who you are, or if so much as one cookie lands in the jar. A tracking script can't creep in quietly; it would stop the release.
- And a page that doesn't exist now says so properly: a mistyped or long-gone article address answers with a true 404, not a soft page pretending — which is what keeps search engines from indexing ghosts. Small, but it's the difference between a library and a pile.
The rehearsal reaches the partner link
- This morning's robot user learned the one journey it couldn't yet walk — the most personal machinery in Kalcius, and the part where a mistake would cost the most trust. Before every update now, two fresh accounts in two real browsers live out the whole partner link: he sets up the profile, chooses what to share and what to hold back, and sends the invite; she opens the emailed link, reads the plain statement of exactly what she'd be accepting — including what he chose not to share, named to her before she decides — accepts, uses her view, corrects the calendar herself, and ends the link with two clicks. Every step through the actual pages, against a real copy of the system.
- Real email is part of the rehearsal now. The suite runs its own mail server, and Kalcius genuinely delivers to it — the verification email, the invite with its disclosure of what's shared. The robot clicks the same links a real inbox would carry, so a broken link in an email can't ship any more than a broken button can. That includes the gate in front of it all: invites refuse to go out until your address is proven to be yours, and the refusal — then the emailed proof, then the send — is rehearsed on every build.
- The promises under test are the ones this feature lives on. A note he keeps to himself never appears anywhere on her pages. Her own corrections land — and they cannot erase what she was never shown, so a withheld note survives her edits untouched. Her page always names what's held back, never silently shows less. And her exit is unilateral and immediate: the moment she ends the link, her side shows nothing and his side is honestly back to logging alone. Each of those is now checked, word for word and click by click, before any change reaches you.
A robot walks through the app before you do
- Kalcius already ran hundreds of automated checks on its inner machinery before any update shipped. What it didn't have was a check that uses the app the way you do — through the actual pages, clicking the actual buttons. Now it does: on every change, a real browser signs up as a fresh user, logs a semen analysis and finds it back on the doctor's copy, walks the self-exam flow (including the book-a-GP panel appearing when it must, with the written note shown back word for word), saves and clears a note on the partner calendar, and opens the Today screen checking every panel behind it answers.
- The whole rehearsal runs against a real copy of the system — real database, real API, real pages — never a simulation of one. That matters because of a failure we've been honest about before: in mid-August, four days of updates shipped a web app that asked the server questions it couldn't yet answer. Checks of each half separately said green; only using the two together would have caught it. This suite uses the two together, on every single change.
- The bug Diana reported — an edit that looked saved and quietly reverted — now has a browser test walking that exact path on every update, so that class of bug can't ship twice. And like everything else in the safety net, the rehearsal is itself watched: if it goes red, our production watchdog raises an alarm on the build board within the half hour.
The full log
Everything since the first build, month by month — the entries above included.
- September 2026 — 5 entries
- August 2026 — 80 entries
- July 2026 — 5 entries