- storage/pocketbase.go: replace http.DefaultClient (no timeout) with a
dedicated pbHTTPClient{Timeout: 30s} so a slow/hung PocketBase cannot
stall the backend or runner indefinitely
- runner/asynq_runner.go: heartbeat ticker was firing at StaleTaskThreshold
(2 min) == the Docker healthcheck deadline, so a single missed tick would
mark the container unhealthy; halved to StaleTaskThreshold/2 (1 min)
Two bugs prevented asynq mode from working correctly on the homelab runner:
1. No healthcheck file: asynq mode never writes /tmp/runner.alive, so
Docker healthcheck always fails. Added heartbeat goroutine that
writes the file every StaleTaskThreshold (30s).
2. Translation tasks not dispatched: translation uses ClaimNextTranslationTask
(PocketBase poll queue), not Redis/asynq. Audio + scrape use asynq mux,
but translation sits in PocketBase forever. Added pollTranslationTasks()
goroutine that polls PocketBase on the same PollInterval as the old
poll() loop.
All Go tests pass (go test ./... in backend/).