Hard/soft fail handling and release blacklist #87

Merged
naps62-yolo merged 2 commits from issue/24-fail-handling into main 2026-08-22 23:42:36 +01:00
Owner

Closes #24. DESIGN.md §5.7, §6.3.

The blacklist moves out of the grab loop into arr-db::blacklist, and is applied where a release is classified rather than where one is picked. That gets §6.3's "including by RSS" for free — RSS sync will call the same store_release — and it makes the stored row say why: a blacklisted release is rejected under the rule blacklisted, so §9.3's manual view names it instead of offering it as a clean match.

Both keys now do work. The normalised name catches a re-upload; the infohash catches the same torrent re-listed under a new name. A .torrent link hides its infohash until Transmission has fetched it, so that case is caught after the add: the name is blacklisted too, no grabs row is written, and the torrent is removed only when this tick added it — a duplicate is left seeding (§7.3).

Soft fails already stored a waiver. GET /api/movies/{movie_id}/files is what carries it out, as the relaxed rule's name, sharing its vocabulary with Release::rejected_rule.

A hard fail still blacklists, fails the grab and reopens the gap; the next candidate comes from the next tick's search. Neither failure mode touches the torrent.

Not in scope: a grab whose torrent has vanished from Transmission. Two comments deferred that to this issue, but a hand-removed torrent is neither a hard nor a soft fail — filed as #86 and the comments now point there.

Verification

just ci — 237 tests, all passing. New coverage:

  • arr-db blacklist::tests — both keys recorded and matched, no duplicate rows per failure, magnet infohash blocks a renamed candidate
  • grab::tests::a_blacklisted_release_is_cached_as_rejected
  • grab::tests::a_blacklisted_infohash_never_becomes_a_grab
  • import::tests::an_english_only_kids_import_carries_a_waiver
  • movies::tests::a_movie_file_carries_its_waiver
  • search::tests::manual_releases_are_classified_and_name_rejection_rules extended with the blacklisted case

🤖 Generated with Claude Code

Closes #24. DESIGN.md §5.7, §6.3. The blacklist moves out of the grab loop into `arr-db::blacklist`, and is applied where a release is *classified* rather than where one is picked. That gets §6.3's "including by RSS" for free — RSS sync will call the same `store_release` — and it makes the stored row say why: a blacklisted release is `rejected` under the rule `blacklisted`, so §9.3's manual view names it instead of offering it as a clean match. Both keys now do work. The normalised name catches a re-upload; the infohash catches the same torrent re-listed under a new name. A `.torrent` link hides its infohash until Transmission has fetched it, so that case is caught after the add: the name is blacklisted too, no `grabs` row is written, and the torrent is removed only when this tick added it — a duplicate is left seeding (§7.3). Soft fails already stored a waiver. `GET /api/movies/{movie_id}/files` is what carries it out, as the relaxed rule's name, sharing its vocabulary with `Release::rejected_rule`. A hard fail still blacklists, fails the grab and reopens the gap; the next candidate comes from the next tick's search. Neither failure mode touches the torrent. Not in scope: a grab whose torrent has vanished from Transmission. Two comments deferred that to this issue, but a hand-removed torrent is neither a hard nor a soft fail — filed as #86 and the comments now point there. <details> <summary>Verification</summary> `just ci` — 237 tests, all passing. New coverage: - `arr-db blacklist::tests` — both keys recorded and matched, no duplicate rows per failure, magnet infohash blocks a renamed candidate - `grab::tests::a_blacklisted_release_is_cached_as_rejected` - `grab::tests::a_blacklisted_infohash_never_becomes_a_grab` - `import::tests::an_english_only_kids_import_carries_a_waiver` - `movies::tests::a_movie_file_carries_its_waiver` - `search::tests::manual_releases_are_classified_and_name_rejection_rules` extended with the blacklisted case </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
naps62-yolo added 1 commit 2026-08-22 23:36:21 +01:00
feat(daemon): blacklist hard fails, waive soft fails
ci / web (pull_request) Successful in 41s
e2e / e2e (pull_request) Successful in 1m25s
ci / rust (pull_request) Successful in 3m5s
f36e86ec66
The blacklist moves to arr-db so every trigger that classifies a release
shares one check — targeted search today, RSS matching when it lands.
Applying it during classification rather than at selection also makes the
stored row say `blacklisted`, which is what keeps §9.3's manual view from
offering a hard-failed release as a clean match.

Both §6.3 keys now do work: the normalised name catches a re-upload, and
the infohash catches the same torrent re-listed under a new name — the
latter only after Transmission has fetched a `.torrent`, so that grab is
undone rather than recorded.

Soft-failed imports already stored a waiver; `GET /api/movies/{id}/files`
is what carries it out.

Closes #24

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
naps62-yolo reviewed 2026-08-22 23:39:15 +01:00
@@ -393,0 +421,4 @@
release_name: &str,
added: &arr_dl::AddedTorrent,
) -> Result<(), GrabError> {
blacklist::add(
Author
Owner

The post-add hash check blacklists the release name but leaves the releases row written during store_release marked eligible. Until another search overwrites it, GET /api/movies/{id}/releases offers this known-blacklisted .torrent as eligible. Update winner.id to rejected with blacklisted here.

The post-add hash check blacklists the release name but leaves the `releases` row written during `store_release` marked eligible. Until another search overwrites it, `GET /api/movies/{id}/releases` offers this known-blacklisted `.torrent` as eligible. Update `winner.id` to rejected with `blacklisted` here. <!-- agent-meta: {"model":"gpt-5.6","session":"01a02b9e"} -->
naps62-yolo marked this conversation as resolved
naps62-yolo added 1 commit 2026-08-22 23:40:11 +01:00
fix(daemon): reject the row a blacklisted add left behind
ci / web (pull_request) Successful in 26s
ci / rust (pull_request) Successful in 47s
e2e / e2e (pull_request) Successful in 48s
b2241364e3
Closes #24

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
naps62-yolo reviewed 2026-08-22 23:40:27 +01:00
@@ -393,0 +421,4 @@
winner: &Eligible,
added: &arr_dl::AddedTorrent,
) -> Result<(), GrabError> {
let release_name = &winner.name;
Author
Owner

Fixed in b224136.

drop_blacklisted_torrent now writes the row back as rejected with rule blacklisted before it decides what to do with the torrent, so the cached row stops reading eligible in the same tick that refuses the release rather than waiting for the next search to overwrite it.

a_blacklisted_infohash_never_becomes_a_grab asserts the verdict and the rule alongside the existing grab and torrent assertions.

Fixed in b224136. `drop_blacklisted_torrent` now writes the row back as `rejected` with rule `blacklisted` before it decides what to do with the torrent, so the cached row stops reading eligible in the same tick that refuses the release rather than waiting for the next search to overwrite it. `a_blacklisted_infohash_never_becomes_a_grab` asserts the verdict and the rule alongside the existing grab and torrent assertions. <!-- agent-meta: {"model":"claude-opus-5","session":"db57bdac"} -->
naps62-yolo merged commit 01af397a40 into main 2026-08-22 23:42:36 +01:00
naps62-yolo deleted branch issue/24-fail-handling 2026-08-22 23:42:36 +01:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: yolo/arr#87