Build log

What got built,
and what it cost.

One person, one month. This page exists because a company with no users has exactly one honest way to show momentum, and this is it. Updated when something ships — not weekly for its own sake.

Every figure here is a count from the repository or a test run, not an estimate. Where something broke, it says so.

  1. 26 July

    The website was claiming things the product does not do.

    Rebuilt this site from scratch and found, while doing it, that the old one asserted "unlimited sync pipelines" for Notion, Slack and Linear — none of which are built — an "Adversarial L3 Verification Engine" that does not exist, and 98.4% diarization accuracy that nobody had ever measured. It also told visitors we never store recordings while its own privacy page said a .wav is kept forever. All of it is gone.

    Also deleted 3.9 MB of decorative photographs, four Google Font requests, and nine divs that had no styling at all and rendered as invisible boxes inside every product mockup. The page went from that to 162 KB with zero third-party requests.

    Lesson worth keeping: nobody had lied on purpose. Marketing copy had simply been written ahead of the code and never walked back. The fix was a script that greps the built site for the specific claims we are not allowed to make.

  2. 25 July

    The database emptied itself, and I could not prove why.

    Between two launches, the local store went from eight meetings with transcripts, notes and memory to completely empty — and the app booted into the first-run wizard as if nothing had ever been there. The audio files were still on disk. Nothing in the code explained it, and I never established the cause.

    What the incident did prove is structural: there was no durability machinery at all. No corruption check, no backup, no way for the app to tell "brand new" apart from "everything is gone". A zero-length file was silently treated as a fresh install, and the startup routine that trims old data was capable of deleting the product's own memory.

    So: verified encrypted backups that are opened and row-counted before they are trusted, three rotating generations, typed open errors that distinguish a wrong key from a corrupt file, and a loss detector that notices audio on disk with no meetings in the database and refuses to pretend it is a first run. Plus one rule written into the code — a backup that has rows is never overwritten by a source that has none.

    Nine new tests, all of which fail against the previous day's code.

  3. 25 July

    Every threshold now has to answer to a number.

    Built a word-error-rate and extraction-quality harness with pass/fail gates, because up to that point every claim about transcription quality was an assertion. It scores WER and CER with a real alignment, term recall for the proper nouns that actually matter, and extraction quality — grounded rate, owner recall, owner precision.

    It reports honestly on empty inputs. Right now it says 0 cases scored, 2 awaiting truth, because a reference transcript has to be written by a human who listened to the audio, and I have not done that yet. A harness that scored itself would be worse than no harness.

  4. 25 July

    A threshold that lied about its own units.

    The meeting endpointer documented a 1200 ms minimum. It measured the whole buffer — which includes pre-roll and the entire trailing silence — so in practice it admitted utterances containing about 320 ms of speech. The gate actually holding the line was a different one, at roughly 480 ms.

    Fixed additively: a new threshold stated in speech milliseconds, defaulted to the value that was really in force, so behaviour did not change on anyone. The buffer limit stayed a buffer limit, because it bounds how much audio one request carries.

    Found by measuring, not by reading. This is the entry I would point at if someone asked why the eval harness exists.

  5. 25 July

    A meeting you cannot see is a meeting with no note.

    Recording started from the consent nudge worked — audio captured, transcript produced — but the main window never adopted it. No cockpit, no transcript, no way to press stop. And because the note is written when a meeting ends, a meeting that could not be ended produced nothing at all.

    The window now adopts a recording that is already in progress instead of only ones it started itself, and the note-writing step runs for every meeting that has a transcript rather than only cleanly-finished ones. Interrupted sessions are recovered at the next sign-in.

  6. 25 July

    The flywheel finally turns for meetings too.

    Dictation had it from the start: the words on your screen ride each transcription request, so a name is spelled right the first time it is said, and they are written back so the next session starts already biased. Meetings — the surface where the slides and the shared documents full of proper nouns actually are — had neither.

    Now a shared screen reading refreshes through the whole meeting and both transcription workers read it, terms are written back on every probe so ten minutes of learning survives a crash, and the extraction step finally receives what earlier meetings taught it instead of starting from zero each time.

  7. 24 July

    Your own voice, transcribed as the other person's.

    A meeting is captured as two streams — the microphone and the system output. Played through speakers, the other party's voice arrives in the microphone too, so the transcript attributed their sentences to me. Three layers went in: acoustic echo cancellation in the signal domain, a correlation gate that scores microphone against system audio and skips a request entirely when an utterance is mostly echo, and conditioning before either.

    Same day: transcription started returning 429s under real load. Root cause was two workers and three other surfaces all hitting the same rate limit with no shared pacing. One global limiter now, with labelled waits so the logs say which surface is queuing.

  8. 24 July

    It notices the call before you do.

    A background poll notices a conferencing app taking the microphone and offers to record. Consent-first by construction: the nudge never starts recording on its own, the click does. Browsers sit behind a continuous-use delay so a voice search does not trigger it.

  9. 23 July

    Working pipes, no water.

    Dictation worked. Questions worked. The agent worked. And none of them shared anything: the memory was a 56-line interface with no implementation behind it, so three good features sat side by side and the whole thing did not feel like one product. Twenty-nine commits that day, and the important one was the memory spine — facts with provenance, full-text search, and vocabulary that persists across sessions, wired into all three surfaces at once.

    The plan document written that morning says it plainly: memory was 0 lines of code, and that was the root of "it does not feel like my vision".

  10. 21–22 July

    Speech to text to somewhere it matters.

    Audio capture, voice-activity chunking, transcription, an anti-hallucination gate, and the part everyone underestimates — putting the text into another application reliably, which needed a low-level keyboard hook, a modifier drain, and a pinned target window. Then screen perception, question mode with its dark-glass card, and agent mode delegating real work to Claude Code with an approval gate in front of it.

  11. 20 July

    One commit, no code.

    The first commit in this repository is a document, not a program. The previous attempt had not failed on code — the code was fine — it failed because the goal kept moving and nothing was written down to stop it. So the anchor came first: what this is, what it will not be, and the rule that a direction change edits the document before it edits the code.

    Every feature since has had to justify itself against that file. Twice it has stopped me building something.

77 commits · ~29,000 lines · 260 tests · 20–26 July 2026

If you want the next one in your inbox rather than checking back, join the list. It is the same list the first build goes to.