From 7b4d7358f5f1fda5235581d5ae8725fb12ce93da Mon Sep 17 00:00:00 2001 From: Daniel Young Date: Wed, 5 Aug 2026 17:02:26 -0400 Subject: [PATCH 1/3] [tacet] Add the tacet cask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Push-to-talk dictation, distributed as a signed and notarized .app. The Mac App Store is not an option for it: pasting the transcript at the cursor means posting synthetic events into other apps, which the sandbox forbids and no entitlement grants. Developer ID plus a tap is the distribution path for this category. Not a formula: the artifact is a prebuilt app bundle whose notarization is the point. Building from source would discard the stapled ticket. Deliberately no `depends_on formula: "whisper-cpp"`. One bundle plays two roles, and only `tacet serve` needs whisper — a laptop that is purely a client, talking to a server on another Mac, should not build it. `uninstall launchctl:` covers both agents, since the setup scripts load them and an uninstall that leaves a service running is worse than one that misses a file. Solves: install without cloning the repo and dragging a zip Tests: brew style and brew audit --online are clean; the installers in 0.1.1 detect this cask and use its bundle rather than rebuilding --- Casks/tacet.rb | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Casks/tacet.rb diff --git a/Casks/tacet.rb b/Casks/tacet.rb new file mode 100644 index 0000000..08f75bc --- /dev/null +++ b/Casks/tacet.rb @@ -0,0 +1,60 @@ +cask "tacet" do + version "0.1.1" + sha256 "72fbbde28665aae8e2d18c73b711f2b3b7e2c80fb75a86b32e7804743a37f14e" + + url "https://github.com/DRYCodeWorks/tacet/releases/download/v#{version}/Tacet.zip", + verified: "github.com/DRYCodeWorks/tacet/" + name "Tacet" + desc "Push-to-talk dictation that pastes the transcript at the cursor" + homepage "https://github.com/DRYCodeWorks/tacet" + + livecheck do + url :url + strategy :github_latest + end + + depends_on macos: :ventura + + app "Tacet.app" + + # Deliberately NOT `depends_on formula: "whisper-cpp"`. One bundle plays two + # roles: the menu-bar agent, and `tacet serve`, which is the only part that + # needs whisper. Making it a hard dependency would build a large formula on + # every laptop that is only ever a client, talking to a server on another + # machine — the two-machine setup this is designed around. + uninstall launchctl: [ + "com.drycodeworks.tacet", + "com.drycodeworks.tacet-whisper", + ] + + zap trash: [ + "~/.config/tacet", + "~/Library/LaunchAgents/com.drycodeworks.tacet-whisper.plist", + "~/Library/LaunchAgents/com.drycodeworks.tacet.plist", + ] + + caveats do + <<~EOS + This cask installs the app bundle. Tacet still needs one-time setup to + write its config and load its launchd agents: + + Server (the machine that transcribes — needs `brew install whisper-cpp`): + ./install-server.sh from a clone of the repo + + Client (the machine you dictate from): + ./install-client.sh from a clone of the repo + + Both detect this cask and use the bundle Homebrew installed rather than + rebuilding one, so you do not end up with two copies and they will not + re-sign a notarized app. Run either with --doctor to diagnose a boundary + that is not working. + + Tacet needs Accessibility (to paste at the cursor) and Microphone + permission. macOS only offers these when the app asks, so grant them at + the first hotkey press — they cannot be pre-approved. + + Set `whisper.prompt` in ~/.config/tacet/config.toml to your own jargon. + It is empty by default and is the cheapest accuracy win available. + EOS + end +end From 2b84d6dbdbae618ef529d1c0dd8cf416387d5530 Mon Sep 17 00:00:00 2001 From: Daniel Young Date: Wed, 5 Aug 2026 17:07:29 -0400 Subject: [PATCH 2/3] [ci] Actually install a changed cask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The same failure as d6c45e9, in a new place. That commit fixed a gate that tested nothing because the diff was empty; this one tests nothing because test-bot has no cask step at all. `--only-formulae` on a cask-only PR prints `testing_formulae (none)` and goes green — observed on the tacet cask PR, three passing legs, zero cask installs. --only-tap-syntax does cover casks, but only through style and audit. Neither installs, so a wrong sha256, an unreachable url, or an `app` stanza naming a bundle the archive does not contain all pass CI and then fail for every user. macOS legs only, since casks do not install on Linux. Solves: a cask gate that silently installed nothing Tests: this PR is the check — it should now install and remove tacet --- .github/workflows/tests.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 04b4938..6958b6d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -48,6 +48,31 @@ jobs: - run: brew test-bot --only-formulae if: github.event_name == 'pull_request' + # test-bot has no cask equivalent — `--only-formulae` reports + # `testing_formulae (none)` for a cask-only change, and the tap-syntax + # step above covers style and audit but never installs anything. So a + # wrong sha256, an unreachable url, or an `app` stanza naming a bundle + # that is not in the archive all pass CI and fail for every user. + # Install what changed, then uninstall it. + - name: Install and uninstall changed casks + if: github.event_name == 'pull_request' && runner.os == 'macOS' + run: | + tap="$(brew --repo drycodeworks/tap)" + git -C "$tap" fetch -q origin "${{ github.base_ref }}" + changed="$(git -C "$tap" diff --name-only \ + "origin/${{ github.base_ref }}...HEAD" -- Casks || true)" + if [[ -z "$changed" ]]; then + echo "No cask changes in this PR." + exit 0 + fi + while read -r path; do + [[ -n "$path" ]] || continue + cask="$(basename "$path" .rb)" + echo "==> Installing drycodeworks/tap/$cask" + brew install --cask "drycodeworks/tap/$cask" + brew uninstall --cask "drycodeworks/tap/$cask" + done <<< "$changed" + - name: Upload bottles as artifact if: always() && github.event_name == 'pull_request' uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 From 9fde2dcce835b31ea58b9bd1629b0855fd5a5f24 Mon Sep 17 00:00:00 2001 From: Daniel Young Date: Wed, 5 Aug 2026 17:12:01 -0400 Subject: [PATCH 3/3] [docs] Document the cask, and why casks need a PR too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tap has casks now, and the README described only formulae — down to the install line, the Brewfile example, and the section heading. The PR requirement applies to both, for different reasons worth keeping separate: formulae because test-bot's diff is empty on a push to main, casks because test-bot has no cask step at all. Also records that `brew audit --new`'s 75-star "notable" gate is an upstream homebrew-cask rule and is expected to fail here. Solves: a tap README that documented half of what the tap ships Tests: none — documentation --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e978fed..39a31db 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,16 @@ Or `brew tap drycodeworks/tap` and then `brew install tailctl`. +Casks install the same way, with `--cask`: + +`brew install --cask drycodeworks/tap/tacet` + Or, in a `brew bundle` `Brewfile`: ```ruby tap "drycodeworks/tap" brew "tailctl" +cask "tacet" ``` ## Formulae @@ -18,9 +23,16 @@ brew "tailctl" - [`tailctl`](https://github.com/DRYCodeWorks/tailctl) — per-identity Tailscale networking for parallel sessions on a single Mac. -## Changing a formula +## Casks + +- [`tacet`](https://github.com/DRYCodeWorks/tacet) — push-to-talk dictation that + pastes the transcript at the cursor. Ships as a signed and notarized app, so + it is a cask rather than a formula: building from source would discard the + stapled ticket. -**Land formula changes via pull request, not a direct push to main.** +## Changing a formula or cask + +**Land changes via pull request, not a direct push to main.** `brew test-bot` picks which formulae to build by diffing `HEAD` against `origin/main`. On a push to main those are the same commit, so the diff is empty @@ -28,7 +40,12 @@ and CI reports green having built nothing (`testing_formulae (none)`). Only a PR gives it a base to diff against, and only then does CI actually install, test, and audit the formula. -Bumping a version: +Casks have the same requirement for a different reason: `test-bot` has no cask +step at all, so `--only-formulae` reports `testing_formulae (none)` for a +cask-only change and passes. CI installs changed casks in a separate step that +also diffs against the PR base. + +Bumping a formula version: ```bash brew livecheck drycodeworks/tap/ # is there a new release? @@ -38,6 +55,19 @@ brew test drycodeworks/tap/ brew audit --strict --online drycodeworks/tap/ ``` +Bumping a cask version: + +```bash +brew livecheck --cask drycodeworks/tap/ +# update version + sha256 (shasum -a 256 on the release asset) +brew install --cask drycodeworks/tap/ +brew audit --cask --online drycodeworks/tap/ +``` + +`brew audit --new` additionally requires the upstream repository be "notable" +(75+ stars). That gate is for submissions to homebrew-cask, not for this tap — +expect it to fail here and ignore it. + ## Documentation `brew help`, `man brew` or check [Homebrew's documentation](https://docs.brew.sh).