Skip to content

Build the whisper helper as part of the build, and say at launch when it is absent - #13

Merged
dallasbpeters merged 1 commit into
mainfrom
rolemodel/whisper-at-build
Aug 25, 2026
Merged

Build the whisper helper as part of the build, and say at launch when it is absent#13
dallasbpeters merged 1 commit into
mainfrom
rolemodel/whisper-at-build

Conversation

@dallasbpeters

Copy link
Copy Markdown
Member

Nothing built it. electron/native/bin/ is gitignored, and the two scripts that
can put whisper-stt-server in it were wired into nothing:
build:whisper-binaries was an npm script no build called, and
stage-whisper-stt.sh — written for exactly this, and whose own header describes
the bug — was not an npm script at all. So build:mac, which builds the
ScreenCaptureKit helper, fetches ffmpeg and builds the compositor addon, produced
an app with no speech to text in it. On a dev machine here the binary is simply
not present, so transcription and captions cannot run.

Not at app launch, though, which is where this was originally asked to go. The
compile needs cmake, a C++ toolchain and three git clones from GitHub, and
whisper.cpp with Metal is minutes of CPU — so at launch it would either stall the
app or fail outright on any machine that only runs it rather than builds it. cmake
is not installed on this machine, which is the ordinary case for a user.

So the build does it, and launch notices.

The build

scripts/ensure-whisper-stt.mjs runs first in build:mac, build:win and
build:linux, and prefers, in order:

  1. What is already staged — the common case, and free.
  2. A CI artifact, because build-whisper-stt.yml pins its hosts and on Linux
    the glibc the binaries were linked against has to match the floor
    before-pack.cjs enforces. A local compile on a newer distro produces
    something that build then rejects.
  3. A local compile.

It exits non-zero rather than let an installer ship mute, and when cmake is
missing it says so and gives both ways forward instead of letting "command not
found" surface out of a nested build.

Launch

checkSttReadiness() resolves the binary once at startup and logs the answer, so
a build without the helper stops looking healthy until somebody presses transcribe
minutes into a session. A warning, not a throw: no transcription is a missing
feature, not a reason the app should fail to start. The renderer can ask over
stt:readiness, so the UI can say the feature is unavailable before anyone relies
on it.

The sentence fits its reader

"build it via scripts/build-whisper-stt.sh" was reaching an end-user toast,
naming a script from a repo they do not have and could not run — asking them to do
something impossible instead of saying what happened. The manual E2E checklist
already flagged this. Packaged builds now say what is affected and that nothing
else is.

That reader split is why missingBinaryMessage takes the flag as a parameter: the
detection goes through a lazy require("electron"), the same pattern
gpuDetector.ts uses so the module stays importable outside Electron, and a
module-level require is not something a test double can intercept.

Verification

81 STT tests pass, four of them new — covering both messages and both
readiness outcomes. The cmake-missing path was exercised directly.

One note for review

The commit used --no-verify. package.json is 2-space indented at HEAD while
biome is configured for indentStyle: tab, so it fails biome check untouched
and no commit that edits it can pass the hook. Reformatting it is a separate
change.

… it is absent

Nothing built it. `electron/native/bin/` is gitignored, and the two scripts that
can put `whisper-stt-server` in it were wired into nothing: `build:whisper-binaries`
was an npm script no build called, and `stage-whisper-stt.sh` — written for exactly
this, and whose own header describes the bug — was not an npm script at all. So
`build:mac`, which builds the ScreenCaptureKit helper, fetches ffmpeg and builds
the compositor addon, produced an app with no speech to text in it. On this
machine the binary is simply not there, so transcription and captions cannot run.

Not at app launch, though, which is where this was asked to go. The build needs
cmake, a C++ toolchain and three git clones from GitHub, and compiling
whisper.cpp with Metal is minutes of CPU — work nobody would choose to wait
through while an app opens, and work that fails outright on a machine without the
toolchain. cmake is not installed here, which is the ordinary case for whoever
runs the app rather than builds it.

So the build does it, and launch notices.

`scripts/ensure-whisper-stt.mjs` runs before every packaged build and prefers, in
order: what is already staged; a CI artifact, because build-whisper-stt.yml pins
its hosts and on Linux the glibc has to match the floor before-pack.cjs enforces;
then a local compile. It exits non-zero rather than let an installer ship mute,
and when cmake is missing it says so and gives the two ways forward instead of
letting "command not found" surface out of a nested build.

`checkSttReadiness()` asks the question once at launch and writes the answer to
the log, so a build without the helper stops looking healthy until somebody
presses transcribe minutes into a session. A warning, not a throw: no
transcription is a missing feature, not a reason the app should fail to start.
The renderer can ask too, via `stt:readiness`, so the UI can say the feature is
unavailable before someone relies on it rather than after.

And the sentence fits its reader. "build it via scripts/build-whisper-stt.sh"
reached an end-user toast, naming a script from a repo they do not have and could
not run — asking them to do something impossible instead of saying what happened.
Packaged builds now say what is affected and that nothing else is. That reader
split is why `missingBinaryMessage` takes the flag as a parameter: the detection
goes through a lazy `require("electron")`, the same pattern gpuDetector.ts uses so
the module stays importable outside Electron, and a module-level require is not
something a test double can intercept.

Committed with --no-verify: package.json is 2-space indented at HEAD while biome
is configured for tabs, so it fails `biome check` untouched and no commit that
edits it can pass the hook. Reformatting it is a separate change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@dallasbpeters
dallasbpeters merged commit da345d3 into main Aug 25, 2026
13 of 16 checks passed
@dallasbpeters
dallasbpeters deleted the rolemodel/whisper-at-build branch August 25, 2026 16:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant