Skip to content

Document engine APIs, settings, update checking, and embedding constraints - #135

Merged
alexkroman merged 2 commits into
mainfrom
claude/blurtengine-md-updates-itr29k
Aug 13, 2026
Merged

Document engine APIs, settings, update checking, and embedding constraints#135
alexkroman merged 2 commits into
mainfrom
claude/blurtengine-md-updates-itr29k

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

What & why

This expands BLURTENGINE.md with comprehensive documentation of the engine's public APIs and design constraints that were previously undocumented or scattered across source comments.

New sections:

  • Quick start: Clarified package consumption (both remote and local paths) and added a note about version tags being app releases, not engine releases, with a forward reference to embedding constraints.

  • Record cues: Documented RecordingCueGate (the edge detector that prevents chime retriggering), SoundPack (the sound catalog and its generation pipeline), and the fact that audio files live in the app bundle, not the package.

  • Settings and persistence: Added a table of all UserDefaults-backed stores (TriggerKeyStore, SoundPackStore, KeyTermsStore, etc.), their keys, and defaults. Explained the decode-with-default pattern, the single DefaultsKey enum that powers resetAll(), and the string helpers (trimmedNonEmpty(), prefix(maxUTF8Bytes:)) that enforce the "usable text" rule.

  • Update checking: Documented UpdateChecker, SemanticVersion, AutomaticUpdateCheck, and UpdateAlertContent — the decision half of self-update that lives in the engine because the AppKit shell has no test target.

  • Embedding outside Blurt: A new section flagging three things that are Blurt-specific and have no opt-out today:

    • Blurt's identity is baked into engine defaults (Keychain service, logging subsystem, defaults keys, log paths, update URL, product name).
    • The Update/ types aren't part of a dictation engine and live here only for test coverage reasons.
    • SoundPack ships metadata without audio files, which live in the app bundle.

Each subsection explains the constraint, why it exists, and what a second host would need to do to work around it or fix it properly.

  • Accessibility grants and signing identity migration: Documented SigningIdentityMigration and SigningIdentity, explaining the macOS designated-requirement wrinkle and why the migration key lives outside the settings roster.

How it was tested

  • scripts/check.sh passes (or CI will, if I'm not on a Mac)
  • I read AGENTS.md and this doesn't reintroduce anything deliberately removed
  • Docs updated (this is a docs-only change)

No code changes; documentation only. Existing tests and CI coverage remain unchanged.

https://claude.ai/code/session_012cpCgfMDhNBQYFQS2zgDGy

claude added 2 commits August 13, 2026 17:47
BLURTENGINE.md was accurate but incomplete: a set of public API added
since its last revision had never been documented at all. Nothing in the
guide contradicted the source — it just stopped short of it.

Adds coverage for:

- Record cues: RecordingCueGate's edge detection, SoundPack/SoundPackStore
  and the generated catalog, and the fact that the cue audio lives in the
  app bundle rather than the package.
- RecentDictations and OverlayOriginStore, alongside the overlay geometry
  they sit next to.
- Settings and persistence: the store roster with each key and what unset
  means, why DefaultsKey + PersistedSettings.resetAll are one edit rather
  than two, and the trimmedNonEmpty / prefix(maxUTF8Bytes:) rules.
- Update checking: UpdateChecker, SemanticVersion, AutomaticUpdateCheck's
  launch gate, and UpdateAlertContent.
- SigningIdentity / SigningIdentityMigration, next to SetupReadiness, plus
  why its defaults key stays outside the reset roster.
- A new "Embedding outside Blurt" section naming the constants that hard-code
  Blurt's identity (Keychain service, log subsystem and paths, defaults
  prefixes, update feed) and which of them a host can actually override.

Docs only; no source changes. scripts/check.sh --portable passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012cpCgfMDhNBQYFQS2zgDGy
Quick start only said "from a checkout or as a local path dependency",
which undersold it — Package.swift is at the repo root with a semver tag
history, so the engine is consumable today. Shows the actual
.package(url:from:) snippet, and flags that the tags are Blurt's app
releases from the DMG pipeline rather than independent engine releases, so
a patch bump says nothing about whether the engine changed.

Expands "Embedding outside Blurt" into the three things that are actually
in the way of a third-party host, each with what closing it would take:

- Blurt's identity baked into engine defaults (Keychain service, os_log
  subsystem, log paths, defaults prefix, update feed, product name) — only
  the update URL has a real seam from outside, and the rest need a
  host-supplied identity value.
- Update/ being Blurt's self-updater inside a dictation library, why it
  lives in the engine at all (the shell has no unit-test target), and why
  a target split has to keep the DefaultsKey reset roster intact.
- SoundPack shipping 192 voices whose cue stems resolve to nothing,
  because all 384 .m4a files are app-side.

Also corrects the catalog size in Record cues to the exact 192.

Docs only; no source changes. scripts/check.sh --portable passes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012cpCgfMDhNBQYFQS2zgDGy
@alexkroman
alexkroman enabled auto-merge August 13, 2026 17:53
@github-actions

Copy link
Copy Markdown
Contributor

Dev build

Download Blurt.app — built from 3603621, Debug-Local,
ad-hoc signed. Expires in 14 days.

Installing it
cd ~/Downloads
unzip -o blurt-dev-build-pr-135.zip   # GitHub wraps every artifact in a zip
unzip -o Blurt-dev-3603621.zip
find Blurt.app -exec xattr -c {} +   # clear quarantine: xattr lost -r in macOS 12.3
rm -rf /Applications/Blurt.app && cp -R Blurt.app /Applications/
open -a Blurt

It is ad-hoc signed and not notarized: Gatekeeper refuses to open it until
the quarantine flag is cleared, and macOS treats it as a different app from a
released Blurt, so you have to re-grant Microphone, Accessibility, and Input
Monitoring. Reinstall the release DMG
when you are done reviewing.

Expect that re-grant once per dev build, including a second build of this
same PR. TCC pins an Accessibility grant to the signature that took it, and an
ad-hoc signature is just a hash of the binary, so every build is a new app as far
as tccd is concerned. Blurt clears the orphaned grant at launch, which is what
keeps the Accessibility step from getting stuck on a Blurt row that is switched on
and still denied. If you are coming from a build old enough to predate that,
clear the grant yourself once:

tccutil reset Accessibility dev.alex.blurt

@alexkroman
alexkroman added this pull request to the merge queue Aug 13, 2026
Merged via the queue into main with commit 86b557e Aug 13, 2026
12 checks passed
@alexkroman
alexkroman deleted the claude/blurtengine-md-updates-itr29k branch August 13, 2026 18:20
alexkroman pushed a commit that referenced this pull request Aug 13, 2026
main moved to d8491e2 (#132 conversation_context/word_boost, #135 engine
docs), which made the PR un-mergeable. Only BLURTENGINE.md conflicted;
the Swift auto-merged and both of this branch's DictationSession changes
(setPhase(.connecting), mic.cancelCapture()) survived intact.

Three doc conflicts, all resolved by taking main's rewritten prose and
re-applying this branch's additions on top:

- The "cleanup happens server-side" bullet is main's verbatim — it now
  describes conversation_context/word_boost, and the version here still
  described the deleted TranscriptionPrompt.
- The projections bullet takes main's text with `.connecting` re-inserted
  into the OverlayUIState list and the menu-bar clause.
- The settled-decisions rows combine both edits: CoreAudio joins the
  allowed-imports line, and transcription steering points at
  ConversationContext rather than the now-deleted TranscriptionPrompt.

Also corrected a sentence main added while this branch was open: its new
"Record cues" section described the chime as firing on the idle→recording
edge, which stopped being the whole story once `.connecting` landed in
front of `.recording`. It now names the connecting→recording edge and why
the chime must not fire at the press.

main did not touch RecordingCueGate, PipelinePhase, OverlayUIState or
MenuBarStatus, so there was no semantic overlap in the projections.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JU1Ln2MKMsF9PJf1FLMQRX
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants