feat: require two recent failures to queue a season

The season branch of the attention queue listed a season on one failed
grab of any age, so `GET /api/queues/attention` returned Rick and Morty
with every season it has and buried the one that needed attention.

Two changes, both stated in DESIGN.md §5.7:

- The season branch now enforces the same bar the episode branch does:
  two grabs that hard-failed on *different* releases.
- A failed grab counts toward the queue for 30 days
  (`arr_db::ATTENTION_WINDOW`). Nothing clears a `grabs` row, so without
  a window the queue only grows and can never be emptied. #181 gave the
  pack guard a backoff curve for the same reason; this is the queue's
  version of §6.2's "it never gives up entirely, it goes quiet". A
  season the operator dealt with stops failing and drops out; one still
  breaking keeps failing (the pack guard retries at worst weekly) and
  stays.

The window applies to all three hard-fail lanes — movie, episode and
season — because DESIGN.md states one rule for the queue, and to the
daemon's needs-a-decision notifier as well as the API, since both read
the same queue and a season-per-failure notification is the same noise
on a different channel. No schema change: `grabs.grabbed_at` already
carries the timestamp.

Gate: `just ci` green (486 tests).
This commit is contained in:
Miguel Palhas
2026-08-25 10:09:07 +01:00
parent 8c3e1c4a92
commit ac0e80c044
10 changed files with 335 additions and 35 deletions
+17
View File
@@ -378,6 +378,23 @@ A policy violation found by `ffprobe` is not one thing.
Neither deletes the torrent. See §7.3.
**Two hard failures make a decision, and only for 30 days.** A movie, an
episode or a season enters the needs-a-decision queue (§9.5) when two grabs
against *different* releases hard-failed on it, and both of those failures
happened within the last 30 days. One bad torrent is not a decision — a
release that hard-failed is blacklisted (§6.3) and the next candidate is
grabbed, which is the system working.
The window is what lets the queue be emptied. Nothing clears a `grabs` row, so
without it the queue only ever grows and the one season that wants attention
sits behind eight that were dealt with months ago. It is the queue's version of
§6.2's "it never gives up entirely, it goes quiet": a target the operator has
dealt with stops producing failures and drops out once the last one ages past
30 days, while a target that is still broken keeps producing them — the pack
guard retries at worst weekly (§6.2) — and stays queued for exactly as long as
it is genuinely broken. Nothing is dismissed by hand and no acknowledgement
state is stored, so there is no second thing to keep correct.
## 6. Sourcing
### 6.1 Prowlarr, per-indexer Torznab