Skip to content

fix: voice broken on user machines + Usage view scroll clipping#44

Merged
hiskudin merged 2 commits into
mainfrom
fix/shebang-and-usage-scroll
May 19, 2026
Merged

fix: voice broken on user machines + Usage view scroll clipping#44
hiskudin merged 2 commits into
mainfrom
fix/shebang-and-usage-scroll

Conversation

@hiskudin

Copy link
Copy Markdown
Collaborator

Summary

Two unrelated fixes that together justify a v1.7.2 patch:

1. Voice was unusable on every CI-built install (release blocker)

The pip-installed `stackvox` script ships with an absolute shebang stamped at install time. On the GitHub Actions runner that shebang points at `/Users/runner/work/.../python3` — a path that doesn't exist on user machines. Any direct invocation of `stackvox` fails with "bad interpreter".

User-visible symptoms:

  • Settings → Voice shows "Voices unavailable" even after a successful model download.
  • Voice synthesis never plays — `Speaker.speak()` exits silently.
  • `launchctl list` shows the daemon agent stuck in a KeepAlive loop with exit code 78.

Fix: in three call sites (`Speaker.speak`, `PanelNav.runStackvoxVoices`, `Bootstrap.writeDaemonPlistIfVenvPresent`) invoke `python3 ` instead of running the script directly. `python3` is a real Mach-O binary that's relocatable, so it works regardless of the build-time shebang. The downloadVoiceModel path already used `python3 -u -c "..."` so it was unaffected — which is exactly why model downloads worked while voice didn't.

2. Usage tab clipping

When all four quota tiers (session / weekly / Opus / Sonnet) are present, the ScrollView's content extends past the panel area and clips behind the PageFooter with no visible scrollbar hint. `.frame(maxHeight: .infinity)` + `.scrollIndicators(.visible)` fixes both.

Test plan

  • After merge, dispatch v1.7.2 build manually (PR fix(ci): manual workflow_dispatch fallback for release.yml #42's workflow_dispatch path).
  • Install v1.7.2 fresh, walk through wizard, download model, confirm voice list populates.
  • Settings → Voice → tweak voice; confirm preview audio plays.
  • Resize panel small + go to Usage tab; confirm all tiers reachable via scroll.

Migration for existing v1.7.1 users

Their daemon plist was written with the broken `[stackvox, "serve"]` ProgramArguments. After auto-updating to v1.7.2, `Bootstrap.writeDaemonPlistIfVenvPresent` only runs on a fresh wizard pass; the existing plist on disk won't auto-rewrite. Options:

  • Worst case for them: voice stays broken until they re-run the wizard.
  • Better: extend `Bootstrap.migrateBundleNameIfNeeded` to also rewrite daemon plists missing the `python3` prefix. Worth a follow-up if we hear complaints, but not blocking this fix.

🤖 Generated with Claude Code

hiskudin and others added 2 commits May 19, 2026 18:36
CI-built bundles were arriving on user machines with a stackvox script
shebang pointing at /Users/runner/work/.../python3 — the CI runner's
filesystem path that pip stamped at install time. Direct execution
failed immediately with "bad interpreter, no such file or directory",
which surfaced in Settings as "Voices unavailable" and made the voice
daemon plist exit with code 78 in a KeepAlive loop.

Fix: never invoke the stackvox script directly. Always go through
`python3 <stackvox-script-path>`. python3 is a real Mach-O binary
(symlinked to python3.12) which is relocatable; calling it bypasses
the script's shebang entirely. Python finds the venv via its own
binary location, so site-packages auto-resolves.

Three call sites updated:
  - Speaker.speak (daemon auto-start + say invocation)
  - PanelNav.runStackvoxVoices (Settings UI voice-list query)
  - Bootstrap.writeDaemonPlistIfVenvPresent (launchd plist's
    ProgramArguments)

Speaker.downloadVoiceModel already invoked `python3 -u -c "..."` so
that path was unaffected — which is why model downloads worked while
voice synthesis didn't.

Existing v1.7.1 installs need to either auto-update past this commit or
manually re-run the bootstrap wizard so the daemon plist gets rewritten
with the python3-prefixed args.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Usage tab's ScrollView wasn't getting an explicit max-height claim,
so when the four quota tiers exceeded the panel's available space the
content rendered past the bottom edge and clipped behind the PageFooter
— with no visible scrollbar to hint at the overflow.

  - .frame(maxHeight: .infinity) forces the scrollview to bound itself
    to the available area rather than expanding to fit content.
  - .scrollIndicators(.visible) overrides macOS's "auto-hide when not
    scrolling" default so users see the affordance immediately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hiskudin hiskudin merged commit 3da4bf5 into main May 19, 2026
4 checks passed
@hiskudin hiskudin deleted the fix/shebang-and-usage-scroll branch May 19, 2026 17:37
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.

1 participant