Skip to content

feat(phase-3): resilience — offline + health + pipe#12

Merged
sonpiaz merged 3 commits into
mainfrom
feat/phase-3-resilience
May 19, 2026
Merged

feat(phase-3): resilience — offline + health + pipe#12
sonpiaz merged 3 commits into
mainfrom
feat/phase-3-resilience

Conversation

@sonpiaz
Copy link
Copy Markdown
Owner

@sonpiaz sonpiaz commented May 19, 2026

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):

Watch any social video → get an architecture diagram, working component, runnable notebook, or step-by-step cheat sheet — automatically.

Items shipped

Item Where
Audio backend routing helper lib/audio-routing.sh
Model SHA256 pin constants lib/model-checksums.sh
Offline whisper.cpp transcribe path bin/transcribe (local branch)
audio-q rejects WATCH_AUDIO_MODE=local bin/audio-q
Capture user-supplied WATCH_AUDIO_MODE pre-overwrite lib/env.sh
install.sh --with-local install.sh
Upstream platform health probe lib/health.sh
bin/watch integrates health probe + warns on probe fail bin/watch
bin/watch --pipe (JSONL on stdin/stdout) bin/watch
Pipe-mode auto-enable on non-TTY stdin bin/watch
examples/batch-watch.sh examples/batch-watch.sh
Pipe mode addendum docs/output-schema.md
Breakage and recovery section docs/platforms.md
New tests: empty pipe, invalid URL pipe, forced local missing-dep tests/test-output-schema.sh

Routing priority (offline mode)

When WATCH_AUDIO_MODE is unset, bin/transcribe picks the first usable backend:

  1. Local whisper.cpp (binary whisper-cli or main on PATH and ~/.watch-cli/models/ggml-large-v3-turbo.bin present)
  2. Kyma (KYMA_API_KEY set)
  3. BYOK Groq (GROQ_API_KEY set)
  4. None → exit 2 with tag=missing-config

An explicit WATCH_AUDIO_MODE is a contract: the script never silently falls back. local mode emits no transcribe_cost_usd field in v1 JSON output (per docs/offline-mode.md).

Model SHA256

Pinned 1fc70f774d38eb169993ac391eea357ef47c88757ef72ee5943879b7e8e2bc69 in lib/model-checksums.sh. Sourced from HuggingFace's x-linked-etag header 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 with curl --progress-bar -fL and verifies against the pin; mismatch deletes the partial file. If the upstream blob changes, the first user to download will hit model-checksum-mismatch and the pin can be re-derived from a real download in a follow-up.

Punted

  • No first-run download prompt in bin/transcribe. Per the task brief; explicit install.sh --with-local is the only download path today. TODO comment dropped at the bottom of the --with-local block in install.sh.
  • No live --with-local test 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.
  • SHA256 not re-verified from a downloaded file. Pin comes from the HF header. See above.

Test plan

  • bash -n on all 6 bin scripts + lib/.sh + install.sh + tests/ — pass
  • watch --help mentions pipe — pass
  • transcribe --help mentions WATCH_AUDIO_MODE — pass
  • WATCH_AUDIO_MODE=local transcribe /dev/null → exit 2, tag=missing-dep:whisper-cli — pass
  • printf '' | watch --pipe → exit 0, no stdout — pass
  • printf 'not-a-url\n' | watch --pipe → one JSON line version:1 exit_code:64 error:invalid-url, exit 64 — pass
  • bash tests/test-output-schema.sh — all assertions pass

Reviewer checklist

  • Sub-spec (docs/offline-mode.md) read top to bottom
  • No silent fallback from local → API on any failure path
  • transcribe_cost_usd key absent in local-mode JSON (verify in a follow-up live test)
  • Health probe is a warning, not a gate (user can still proceed)
  • Pipe-mode error objects keep version:1 so JSONL consumers can detect the schema
  • lib/audio-routing.sh honors explicit WATCH_AUDIO_MODE as a contract
  • No new dependencies beyond what install.sh already required (offline mode is opt-in via --with-local)
  • BRANDING.md: no competing-backend names in any new copy

sonpiaz and others added 3 commits May 19, 2026 13:28
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]>
@sonpiaz sonpiaz merged commit 202c37e into main May 19, 2026
2 checks passed
@sonpiaz sonpiaz deleted the feat/phase-3-resilience branch May 19, 2026 20:59
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