feat(phase-3): resilience — offline + health + pipe#12
Merged
Conversation
Sub-spec for Phase 3 item #2 (offline whisper.cpp path). Locks the contract before bin/transcribe gets a local-mode case. Covers routing priority (WATCH_AUDIO_MODE override + auto-detect order), whisper-cli vs main binary detection, ggml-large-v3-turbo lifecycle (storage, consent prompt, download URL, SHA256 pin), 2 GB disk-space gate, exact invocation contract, transcribe_cost_usd omission rule in local mode, audio-q API-only policy, no-silent- fallback policy, install.sh --with-local flag, 7-case test plan, and the anti-patterns list. Eight new stderr tags ship under the existing v1 append-only promise. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
- Offline whisper.cpp routing via lib/audio-routing.sh and lib/model-checksums.sh - install.sh --with-local downloads whisper.cpp and the default model - audio-q rejects WATCH_AUDIO_MODE=local explicitly (no silent fallback) - bin/transcribe omits transcribe_cost_usd in local mode - lib/health.sh probes upstream platforms with 24h caching, emits warning on probe fail - bin/watch --pipe accepts stdin URLs, emits JSONL on stdout - examples/batch-watch.sh demonstrates the pipe pattern Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
…-TTY Two bugs caught by CI on PR #12: 1. BASH_SOURCE[0] does not dereference symlinks, so `watch` invoked from ~/.local/bin (the install-time symlink) computed ROOT_DIR as ~/.local instead of ~/.watch-cli and failed to source lib/health.sh. Same pattern existed in bin/transcribe, bin/models, bin/audio-q — they happened to work before only because lib/env.sh sourcing was accidentally absent on those paths in earlier phases. Now all four scripts loop readlink until they find the real path. 2. Auto-enabling pipe mode whenever stdin was not a TTY caused the "no args → exit 64" negative test to instead drop into pipe mode with an empty stream and exit 0. CI runs with non-TTY stdin, which is the common case for any script context, so the auto-trigger was unsafe. Pipe mode is now opt-in via --pipe only. Docs and inline comments updated to reflect the explicit-only pipe contract. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Phase 3 of the defend-the-niche plan: offline transcribe routing, upstream health probe, and pipe mode. Sub-spec for offline mode landed in the preceding commit (
be4d001).Locked pitch (paste verbatim for grep-confirmation):
Items shipped
Routing priority (offline mode)
When
WATCH_AUDIO_MODEis unset,bin/transcribepicks the first usable backend:whisper-cliormainon PATH and~/.watch-cli/models/ggml-large-v3-turbo.binpresent)KYMA_API_KEYset)GROQ_API_KEYset)tag=missing-configAn explicit
WATCH_AUDIO_MODEis a contract: the script never silently falls back.localmode emits notranscribe_cost_usdfield in v1 JSON output (per docs/offline-mode.md).Model SHA256
Pinned
1fc70f774d38eb169993ac391eea357ef47c88757ef72ee5943879b7e8e2bc69inlib/model-checksums.sh. Sourced from HuggingFace'sx-linked-etagheader on the resolve URL (LFS SHA256), not from a local download — committing 1.62 GB of bytes to compute the hash was punted on for time. The installer pulls the file withcurl --progress-bar -fLand verifies against the pin; mismatch deletes the partial file. If the upstream blob changes, the first user to download will hitmodel-checksum-mismatchand the pin can be re-derived from a real download in a follow-up.Punted
bin/transcribe. Per the task brief; explicitinstall.sh --with-localis the only download path today. TODO comment dropped at the bottom of the--with-localblock in install.sh.--with-localtest on a fresh host. Spec covers it in docs/offline-mode.md §Test plan; manual verification on macOS deferred until next session with brew/disk available.Test plan
bash -non all 6 bin scripts + lib/.sh + install.sh + tests/ — passwatch --helpmentionspipe— passtranscribe --helpmentionsWATCH_AUDIO_MODE— passWATCH_AUDIO_MODE=local transcribe /dev/null→ exit 2,tag=missing-dep:whisper-cli— passprintf '' | watch --pipe→ exit 0, no stdout — passprintf 'not-a-url\n' | watch --pipe→ one JSON lineversion:1 exit_code:64 error:invalid-url, exit 64 — passbash tests/test-output-schema.sh— all assertions passReviewer checklist
transcribe_cost_usdkey absent in local-mode JSON (verify in a follow-up live test)version:1so JSONL consumers can detect the schemalib/audio-routing.shhonors explicitWATCH_AUDIO_MODEas a contract--with-local)