DEV Community

Billie M
Billie M

Posted on Originally published at billiem.uk

I let Codex build and test my first native Mac app

The first public Billie Flow build launched, stayed alive, passed its signature check and could find its source and models. A replacement install still left a new user with no Settings window, which meant there was no visible way to install the local worker.

The process was healthy. The app was unusable.

That gap is the part of my local dictation experiment that I find most useful. I had let Codex work well beyond a repository: it built my first native Mac app, installed it, operated the macOS permission surfaces, exercised physical audio and inspected the clipboard. The interesting failures only appeared because it kept going after the code checks were green.

Testing the installed app changed the work

I started Billie Flow because I use Wispr Flow and wondered whether I could make the useful loop run locally. What led me to it was just curiosity. Could I really?

It was never meant to become a replacement. Wispr Flow gives me enough value that I still use it. I wanted to see whether a personal version could be made, and then I became much more interested in how involved I let Codex become.

In 5.6 it could literally write the thing, build the thing, install the dependencies for the thing, test the thing. For this project, "test" eventually meant opening the installed Swift app, working through microphone permissions with Computer Use, holding the global shortcut and checking whether the resulting text reached the clipboard.

At one point I was lying there waiting for it to work away when speech started coming out of my speakers. Billie Flow recorded it and transcribed it. I do not know what produced that speech, so I am not going to turn the observation into a tidier technical claim. I remember thinking that 5.5 would not have taken the same initiative, but that is an impression rather than a controlled comparison.

A compact Billie Flow HUD reads Recording, 0:01, release to finish, beside a five-bar level meter.

The global shortcut records only while held; releasing it submits the temporary audio for local processing.

The physical run eventually finished with non-empty text on the clipboard, a healthy app and worker, no new crash and no temporary recording left behind.

Billie Flow's HUD reads Copied, Light cleanup, and Ready on the clipboard beside a document icon.

This is the visible end state from the installed app after local recognition and light cleanup completed.

The expected model was not the useful one

The app work started with a model decision. I had an audio-capable Gemma 12B route in my head, but I ran the same 35.3-second voice memo through several local recognition paths before building around that assumption.

I also kept speech recognition separate from cleanup. Otherwise a cleanup model could make a transcript sound polished while preserving the important names the recogniser had already got wrong.

Gemma completed the memo in 258.79 seconds and drifted around an overlapping chunk. MLX Whisper large-v3-turbo produced the most useful recognition in about 3.68 seconds, then Qwen2.5 1.5B ran the selected light-cleanup pass in about 0.62 seconds.

The model I first had in my head was not the right answer. Two smaller models produced the quicker and more useful result for this app. That is deliberately narrow: it was one memo on one machine, and every recognition path still missed at least one important project term. The full Billie Flow model analysis contains the other branches, timings and vocabulary failures.

Three different kinds of green failure

The absent Settings window was not the only thing the replacement-install pass found.

Cleanup was silently falling back to raw recognition because the pinned MLX library was receiving an obsolete argument. The worker request returned success, but the UI was claiming cleanup that had not happened. A final setup check could also exit before the app registered completion and leave setup stuck at verification.

Earlier microphone tests had exposed an actor-isolation crash on the first audio buffer, then a format mismatch while writing the converted WAV. These were not five versions of the same bug. They crossed Swift concurrency, Core Audio, a pinned Python dependency, process lifecycle and visible macOS presentation.

Codex kept following each failure into the layer that produced it. The release gate now checks for an actual first-launch window and warning-free cleanup instead of treating a living process or successful response as sufficient evidence.

A public proof of concept, with a blunt boundary

The source and install-tested v0.2.1 build are public. I want Billie Flow to be usable should you want to, but I do not really care if anyone does.

Billie Flow's Install local speech models dialog says it will download about 3.5 GB from Hugging Face and requires Apple Silicon and macOS 26.

Nothing large starts until Install is chosen; the disclosure also limits the proof of concept to English speech on Apple Silicon and macOS 26.

The boundary is explicit: Apple Silicon, macOS 26, English recognition and about 3.5 GB of consented runtime and model setup. Inference is local after setup, but setup still downloads Python dependencies and fixed models. The app is ad-hoc signed and unnotarised, with no updater, App Store release, compatibility promise or support plan. I am definitely not getting an Apple Developer Programme membership for it at this point.

Billie Flow does not establish that everybody should rebuild the software they already use. It shows that a surprisingly good personal version of this loop can now be made quickly. What I am going to remember is still the oddest part: lying there while Codex decided it needed to test the microphone and speech started coming out of my speakers. It was really really impressive.


Want to talk about something I’ve written or built? Get in touch.

This article was adapted with AI assistance from an original article on billiem.uk. The original article was reviewed before publication.

Top comments (0)