Stable release covering 2.88.0 through 2.89.5. Previous stable: 2.87.0.
New features
- Per-run episode logs. Every processing run writes its pipeline log to disk, and the episode page's Logs card opens any run's log with a level filter, text search, and raw download. Retention defaults to 30 days, configurable globally and per feed (#659, closes #660).
- Low-ad-yield response policy. When an automatic run removes far less ad time than the feed's recent average, MinusPod can rerun detection on its own. Off by default; pick the action globally and override per feed (#659).
- The Settings processing queue lists the whole backlog in the order the worker will claim it, with per-row cancel. Before, a bulk reprocess could look like one item or nothing at all (#659).
- Blocked user agents. A Settings list of agents that must never trigger just-in-time processing; matching requests get a redirect to the origin audio instead of a transcription run (#647, closes #645).
- A pattern's segment category can be set everywhere a pattern is made or edited: the pattern modal, the manual ad editor, and the pattern APIs (#662).
- Community patterns carry a staleness-based trust badge on the Patterns page: active, unproven, or stale (#659).
- Faster chunked transcription. Chunk extraction runs ahead of the GPU, which takes about five minutes off each pass on a 46-minute episode with large-v3 (#659).
- A verification finding that contradicts a kept pass-1 span is held for review instead of silently discarded. The keep still stands; the disagreement is now visible and one approval away from a cut (#647).
- A detection or verification pass with too many failed LLM windows now fails and retries instead of publishing with those windows unexamined, and an all-failed verification no longer reads as a clean scan. Threshold: AD_DETECTION_MAX_FAILED_WINDOW_RATIO, default 0.25 (#659).
Fixes
- Security: closed a DNS-rebinding TOCTOU in the SSRF guard. Outbound fetches now connect to the exact addresses they validated, per request and per redirect hop. SSRF_IP_PINNING=false reverts to the previous behavior if a fetch breaks (#659).
- The shared processing status file corrupted under concurrent workers, dropping the current job and queue display roughly twice a day on a two-worker install. Writes are now atomic and locked across processes (#649).
- Pattern deduplication and deletion no longer lose audio fingerprints, and a fingerprint can no longer outlive its pattern: the column now carries a real foreign key with cascade delete. Existing databases migrate on startup; orphaned fingerprints are preserved in a side table rather than dropped (#649).
- Deduplication no longer lets a disabled pattern delete an active one, and an operator-written pattern now outranks an auto-learned duplicate (#649).
- Disabling a pattern also silences its stored audio fingerprint, and a keep-resolving pattern match no longer trims an overlapping remove detection down to a remainder that left the ad in the audio (#662).
- A failed migration step can no longer commit a half-finished table rebuild or leave foreign keys disabled (#649).
- The queue drainer no longer drops an episode that was re-queued while its own run was still finishing (#659).
Upgrade notes
- Multi-worker installs using the default in-memory rate-limit storage now get a startup warning: limiter counters are per process, so effective limits multiply by worker count. Set RATE_LIMIT_STORAGE_URI to shared storage to keep configured limits exact. Login lockout is database-backed and unaffected (#659).
- Reviewer verdicts now carry a structured is_ad field. On models that emit it, a disputed span the reviewer judges as an ad is cut instead of held for review. Installs running the shipped reviewer prompt unedited pick this up automatically on upgrade (#659).