Skip to content

fix(sonarr): don't require never-downloaded seasons to be unmonitored before show cleanup#2897

Merged
enoch85 merged 5 commits into
developmentfrom
fix/sonarr-delete-show-if-ended
May 14, 2026
Merged

fix(sonarr): don't require never-downloaded seasons to be unmonitored before show cleanup#2897
enoch85 merged 5 commits into
developmentfrom
fix/sonarr-delete-show-if-ended

Conversation

@enoch85

@enoch85 enoch85 commented May 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #2891 (and re-closes #2757). The "Unmonitor and delete season + delete show if empty" action deleted a season's files but never deleted the now-empty show. Two distinct bugs were stacked on top of each other.

Root cause: stale cache (the reason nothing earlier worked)

ExternalApiService.get() caches responses for 20 minutes. getSeriesByTvdbId went through that cache. The empty-show cleanup's "refetch" — which runs immediately after the unmonitor + episode-file deletes — was a cache hit, so it evaluated the pre-mutation snapshot and concluded the show still had content.

Confirmed from daviddanko's Sonarr debug log: after the deletes, no HTTP GET /series?tvdbId= was issued at all — the refetch never hit Sonarr. Every gate-logic change before this was being fed stale input, which is why "reverted to basics" still failed.

Fix: getSeriesByTvdbId / getMovieByTmdbId now read via getWithoutCache. These endpoints are read straight after mutations and also drive rule evaluation — both need current truth. updateMovie's internal read-modify-write read is uncached too, so it can't PUT a stale movie object back to Radarr.

Secondary fix: the allSeasonsUnmonitored gate

Even with a fresh refetch, the no-Seerr fallback required every season to be unmonitored. Sonarr carries every TVDB season on the series — including ones the user never downloaded — and those stay monitored forever, permanently blocking deletion of a genuinely empty, ended show (e.g. a library with only seasons 1–4 of a 10-season show).

Fix: the no-Seerr fallback now requires series.status === 'ended' and no longer requires all seasons unmonitored. The episodeFileCount === 0 file gate and the Seerr-authority path are unchanged. isShowEmpty's 'monitored' mode now also treats a monitored-but-fileless season as empty, and missing season statistics are treated as unknown rather than empty.

Commits

  • 5760c484 — don't require all seasons unmonitored to delete an empty ended show
  • 620292f9 — apply the same fix to the unmonitor-show check
  • 841c4adf — treat missing season statistics as unknown, not empty
  • 252ad047 — read series/movie lookups uncached so the post-mutation refetch is fresh

Note

getSeriesByTvdbId / getMovieByTmdbId are also called by the rule getters (once per media item per rule evaluation), so they no longer benefit from the 20-minute cache there. For local *arr APIs this is negligible per-call, and rule evaluation arguably should run on current data anyway — but it's a deliberate trade of some read-amplification for guaranteed freshness.

@enoch85 enoch85 marked this pull request as draft May 13, 2026 18:59
@enoch85

enoch85 commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

@daviddanko @andrew-kennedy Is this what you had in mind?

@enoch85 enoch85 marked this pull request as ready for review May 13, 2026 19:20
@enoch85

This comment was marked as resolved.

@github-actions

This comment was marked as outdated.

@daviddanko

Copy link
Copy Markdown

Looks good to me, I think. I have shows scheduled for complete deletion in 2, 6 and 8 days. I can report back. Will you push it to stable in that timespan or should I use nightly/development?

@enoch85

enoch85 commented May 13, 2026

Copy link
Copy Markdown
Collaborator Author

@daviddanko Please use the PR tag. Thanks! 🙏

@daviddanko

Copy link
Copy Markdown

Sure thing. I decreased the actions taken by a day, so one show will be handled tmrw early morning. I'll keep you posted @enoch85.

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

@daviddanko post here.

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

Sonarr] Show 'Smallville' still has monitored seasons - skipping show deletion

I read the log again and this is the reason. So it checks if it's monitored and skips deletion if it is. Can you force handle collections again? Maybe it takes two times for it to be deleted if it's now unmonitored in Sonarr?

Seer logs and Sonarr logs would be helpful. Debug please.

@daviddanko

Copy link
Copy Markdown

Same behavior with maintainerr/maintainerr:pr-2897

2:00:01 AMINFO
Started handling of all collections
2:00:01 AMDEBUG
Skipping collection 'Movies' because no media is due for handling
2:00:01 AMDEBUG
Skipping collection 'TV shows (continuing)' because no media is due for handling
2:00:01 AMINFO
Collection handler summary: 3 total (isActive), 0 skipped (Do Nothing), 2 skipped (no due media), 1 queued for handling
2:00:01 AMINFO
Handling collection 'TV shows (ended)'
2:00:01 AMINFO
Unmonitored season 4 from Sonarr show with ID 382
2:00:01 AMINFO
[Sonarr] Removed season 4 from show 'Smallville'
2:00:01 AMDEBUG
[Sonarr] Show 'Smallville' still has monitored seasons - skipping show deletion
2:00:01 AMDEBUG
[checkAutomaticMediaServerLink] Collection "TV shows (ended)" (DB id: 2, mediaServerId: 6765)
2:00:01 AMDEBUG
[checkAutomaticMediaServerLink] getCollection(6765) returned: id=6765, childCount=9
2:00:01 AMDEBUG
[checkAutomaticMediaServerLink] Trusting Plex metadata childCount=9 for collection 6765, keeping it
2:00:01 AMINFO
Removing 1 media items from collection..
2:00:01 AMINFO
Handling collection 'TV shows (ended)' finished
2:00:01 AMINFO
Sending webhook notification
2:00:08 AMINFO
All collections handled. Triggered Seerr's availability-sync because media was altered

Season 4 was the last, it got deleted, but it was kept in sonarr.

Image

Sonarr] Show 'Smallville' still has monitored seasons - skipping show deletion

I read the log again and this is the reason. So it checks if it's monitored and skips deletion if it is. Can you force handle collections again? Maybe it takes two times for it to be deleted if it's now unmonitored in Sonarr?

10:57:09 AMINFOStarted handling of all collections
10:57:09 AMDEBUGSkipping collection 'Movies' because no media is due for handling
10:57:09 AMDEBUGSkipping collection 'TV shows (ended)' because no media is due for handling
10:57:09 AMDEBUGSkipping collection 'TV shows (continuing)' because no media is due for handling
10:57:09 AMINFOCollection handler summary: 3 total (isActive), 0 skipped (Do Nothing), 3 skipped (no due media), 0 queued for handling
10:57:09 AMINFOAll collections handled. No data was altered
10:57:09 AMINFOUpdating collection size cache...

No change in sonarr, but yes, you are right, it's still monitored in sonarr:

image

But should it be? The rule actions I am using says unmonitor:

image

Seer logs and Sonarr logs would be helpful. Debug please.

Unfortunately I do not have sonarr debug logs, only info, which is not helpful. I enabled debug and I can reduce the "Take action after days" if needed:

2026-05-14 01:44:38.5|Info|RssSyncService|RSS Sync Completed. Reports found: 100, Reports grabbed: 0
2026-05-14 02:00:01.0|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E01 - Crusade DVD.avi
2026-05-14 02:00:01.0|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E01 - Crusade DVD.avi
2026-05-14 02:00:01.0|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E02 - Gone DVD.avi
2026-05-14 02:00:01.0|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E02 - Gone DVD.avi
2026-05-14 02:00:01.1|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E03 - Facade DVD.avi
2026-05-14 02:00:01.1|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E03 - Facade DVD.avi
2026-05-14 02:00:01.1|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E04 - Devoted DVD.avi
2026-05-14 02:00:01.1|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E04 - Devoted DVD.avi
2026-05-14 02:00:01.1|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E05 - Run DVD.avi
2026-05-14 02:00:01.1|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E05 - Run DVD.avi
2026-05-14 02:00:01.1|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E06 - Transference DVD.avi
2026-05-14 02:00:01.1|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E06 - Transference DVD.avi
2026-05-14 02:00:01.1|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E07 - Jinx DVD.avi
2026-05-14 02:00:01.1|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E07 - Jinx DVD.avi
2026-05-14 02:00:01.2|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E08 - Spell DVD.avi
2026-05-14 02:00:01.2|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E08 - Spell DVD.avi
2026-05-14 02:00:01.2|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E09 - Bound DVD.avi
2026-05-14 02:00:01.2|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E09 - Bound DVD.avi
2026-05-14 02:00:01.2|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E10 - Scare DVD.avi
2026-05-14 02:00:01.2|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E10 - Scare DVD.avi
2026-05-14 02:00:01.2|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E11 - Unsafe DVD.avi
2026-05-14 02:00:01.2|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E11 - Unsafe DVD.avi
2026-05-14 02:00:01.2|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E12 - Pariah DVD.avi
2026-05-14 02:00:01.2|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E12 - Pariah DVD.avi
2026-05-14 02:00:01.3|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E13 - Recruit DVD.avi
2026-05-14 02:00:01.3|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E13 - Recruit DVD.avi
2026-05-14 02:00:01.3|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E14 - Krypto DVD.avi
2026-05-14 02:00:01.3|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E14 - Krypto DVD.avi
2026-05-14 02:00:01.3|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E15 - Sacred DVD.avi
2026-05-14 02:00:01.3|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E15 - Sacred DVD.avi
2026-05-14 02:00:01.3|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E16 - Lucy DVD.avi
2026-05-14 02:00:01.3|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E16 - Lucy DVD.avi
2026-05-14 02:00:01.3|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E17 - Onyx DVD.avi
2026-05-14 02:00:01.3|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E17 - Onyx DVD.avi
2026-05-14 02:00:01.3|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E18 - Spirit DVD.avi
2026-05-14 02:00:01.3|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E18 - Spirit DVD.avi
2026-05-14 02:00:01.4|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E19 - Blank DVD.avi
2026-05-14 02:00:01.4|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E19 - Blank DVD.avi
2026-05-14 02:00:01.4|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E20 - Ageless DVD.avi
2026-05-14 02:00:01.4|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E20 - Ageless DVD.avi
2026-05-14 02:00:01.4|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E21 - Forever DVD.avi
2026-05-14 02:00:01.4|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E21 - Forever DVD.avi
2026-05-14 02:00:01.4|Info|MediaFileDeletionService|Deleting episode file: /media/shows/Smallville/Season 4/Smallville - S04E22 - Commencement DVD.avi
2026-05-14 02:00:01.4|Info|RecycleBinProvider|Recycling Bin has not been configured, deleting permanently. /media/shows/Smallville/Season 4/Smallville - S04E22 - Commencement DVD.avi

seerr debug logs:
seerr-2026-05-14.log

… ended show

DELETE_SHOW_IF_EMPTY's no-Seerr fallback required `series.status ===
'ended'` AND every season unmonitored. Sonarr carries every TVDB season
on the series object, including ones the user never downloaded — those
stay monitored indefinitely, so the all-seasons-unmonitored check could
be permanently false for a show that is genuinely empty and ended.

Result: an ended show with zero episode files would never be deleted if
the user only ever had a subset of its seasons (issues #2757 / #2891 —
e.g. Smallville, where seasons 1-4 were processed but 5-10 were never
downloaded and remain monitored).

The `episodeFileCount === 0` gate above this check already proves the
show has no files; `ended` confirms no further episodes are coming.
Requiring all seasons unmonitored on top adds no safety, only breakage.
Drop it. The file gate and the Seerr-authority path are unchanged.

Strictly more permissive than before, so no deletion that happened
previously stops happening.
@enoch85 enoch85 force-pushed the fix/sonarr-delete-show-if-ended branch from a0064f4 to 5760c48 Compare May 14, 2026 09:43
@enoch85 enoch85 changed the title fix(sonarr): gate show deletion on intent + ended, not file count fix(sonarr): don't require all seasons unmonitored to delete an empty ended show May 14, 2026
@enoch85

This comment was marked as resolved.

@github-actions

This comment was marked as resolved.

UNMONITOR_SHOW_IF_EMPTY's `unmonitorShowIfEmptyAndEnded` had the same
flaw as the delete path: it required every season to be unmonitored,
but Sonarr carries every TVDB season on the series object and the ones
the user never downloaded stay monitored forever — so a finished show
could never be unmonitored.

This action keeps files, so it can't use a file-count gate like the
delete path. Instead, the monitored check now ignores seasons that hold
zero files: the show counts as empty once no season is still both
monitored AND holding files. A monitored season with files still blocks,
as it should — the user isn't done with it.

Adds regression coverage for both the never-downloaded-seasons case and
the monitored-season-with-files case.
@enoch85 enoch85 changed the title fix(sonarr): don't require all seasons unmonitored to delete an empty ended show fix(sonarr): don't require never-downloaded seasons to be unmonitored before show cleanup May 14, 2026
@enoch85

This comment was marked as resolved.

Review follow-up. season.statistics is optional in Sonarr's response;
`?? 0` made a monitored season with absent statistics look empty,
which could unmonitor an ended show whose monitored season still has
files. A monitored season is now only treated as empty when Sonarr
explicitly reports episodeFileCount === 0 — missing statistics counts
as still having content (conservative).

Also corrects the success log: the predicate allows monitored zero-file
seasons to remain, so "no monitored seasons" became "no monitored
seasons holding files".

Adds a regression test for a monitored season with no statistics.
@github-actions

This comment was marked as outdated.

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

@daviddanko Please try the latest pr-tag. reverted back to basics and had a deeper look at it.

@enoch85

This comment was marked as resolved.

@github-actions

This comment was marked as outdated.

@daviddanko

Copy link
Copy Markdown

So I tried it after this but before this. Same behavior. Although this show (DTF St. Louis) had only 1 season, maybe that's an edge case which wasn't considered. Still in monitored status and visible in sonarr:

image
2026-05-14T10:00:00.151Z [info][Plex Scan]: Recently Added Scan Complete
2026-05-14T10:01:00.010Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:02:00.012Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:03:00.010Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:03:00.012Z [info][Jobs]: Starting scheduled job: Plex Watchlist Sync
2026-05-14T10:04:00.011Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:05:00.002Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:05:00.005Z [info][Jobs]: Starting scheduled job: Plex Recently Added Scan
2026-05-14T10:05:00.005Z [info][Plex Scan]: Scan starting {"sessionId":"3fd456b4-8e0c-445c-ad5d-a55eb0d71b82"}
2026-05-14T10:05:00.074Z [info][Plex Scan]: Beginning to process recently added for library: Movies {"lastScan":1778752800107}
2026-05-14T10:05:00.119Z [info][Plex Scan]: Beginning to process recently added for library: TV Shows {"lastScan":1778752800151}
2026-05-14T10:05:00.163Z [info][Plex Scan]: Recently Added Scan Complete
2026-05-14T10:06:00.012Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:06:00.014Z [info][Jobs]: Starting scheduled job: Plex Watchlist Sync
2026-05-14T10:07:00.011Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:08:00.012Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:09:00.010Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:09:00.012Z [info][Jobs]: Starting scheduled job: Plex Watchlist Sync
2026-05-14T10:10:00.005Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:10:00.012Z [info][Jobs]: Starting scheduled job: Plex Recently Added Scan
2026-05-14T10:10:00.012Z [info][Plex Scan]: Scan starting {"sessionId":"b8c3e160-7813-42a1-a6a0-36df2d6e1fa8"}
2026-05-14T10:10:00.018Z [error][Download Tracker]: Unable to get queue from Sonarr server: Sonarr
2026-05-14T10:10:00.064Z [info][Plex Scan]: Beginning to process recently added for library: Movies {"lastScan":1778753100119}
2026-05-14T10:10:00.112Z [info][Plex Scan]: Beginning to process recently added for library: TV Shows {"lastScan":1778753100163}
2026-05-14T10:10:00.160Z [info][Plex Scan]: Recently Added Scan Complete
2026-05-14T10:11:00.011Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:12:00.003Z [debug][Jobs]: Starting scheduled job: Download Sync
2026-05-14T10:12:00.005Z [info][Jobs]: Starting scheduled job: Plex Watchlist Sync
2026-05-14T10:12:22.647Z [info][Jobs]: Starting scheduled job: Media Availability Sync
2026-05-14T10:12:22.648Z [info][Availability Sync]: Starting availability sync...
2026-05-14T10:12:22.677Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1049471] still exists. Preventing removal.
2026-05-14T10:12:22.737Z [info][AvailabilitySync]: The non-4K show [TMDB ID 76479] still exists. Preventing removal.
2026-05-14T10:12:22.752Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1325734] still exists. Preventing removal.
2026-05-14T10:12:22.805Z [info][AvailabilitySync]: The non-4K show [TMDB ID 273240] still exists. Preventing removal.
2026-05-14T10:12:22.817Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 980431] still exists. Preventing removal.
2026-05-14T10:12:22.831Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1480387] still exists. Preventing removal.
2026-05-14T10:12:22.844Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1154538] still exists. Preventing removal.
2026-05-14T10:12:22.857Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1339658] still exists. Preventing removal.
2026-05-14T10:12:22.879Z [info][AvailabilitySync]: The non-4K show [TMDB ID 61662] still exists. Preventing removal.
2026-05-14T10:12:22.900Z [info][AvailabilitySync]: The non-4K show [TMDB ID 2025] still exists. Preventing removal.
2026-05-14T10:12:22.918Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 687163] still exists. Preventing removal.
2026-05-14T10:12:22.945Z [info][AvailabilitySync]: The non-4K show [TMDB ID 124364] still exists. Preventing removal.
2026-05-14T10:12:22.959Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 690848] still exists. Preventing removal.
2026-05-14T10:12:22.975Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 14161] still exists. Preventing removal.
2026-05-14T10:12:22.990Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 274855] still exists. Preventing removal.
2026-05-14T10:12:23.009Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 840464] still exists. Preventing removal.
2026-05-14T10:12:23.026Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 524047] still exists. Preventing removal.
2026-05-14T10:12:23.042Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1852] still exists. Preventing removal.
2026-05-14T10:12:23.098Z [info][AvailabilitySync]: The non-4K show [TMDB ID 206828] still exists. Preventing removal.
2026-05-14T10:12:23.123Z [info][AvailabilitySync]: The non-4K show [TMDB ID 95557] still exists. Preventing removal.
2026-05-14T10:12:23.140Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1266127] still exists. Preventing removal.
2026-05-14T10:12:23.154Z [info][AvailabilitySync]: The non-4K movie [TMDB ID 1330021] still exists. Preventing removal.
2026-05-14T10:12:23.155Z [info][Availability Sync]: Availability sync complete.
2026-05-14T10:13:00.010Z [debug][Jobs]: Starting scheduled job: Download Sync

sonarr.debug.txt

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

Can you update to latest PR just to confirm... I don't think it makes a difference, but how knows.

@daviddanko

Copy link
Copy Markdown

No change. And I have only one more show to delete :D Will need to download something else for testing. Would you like me to provide the logs?

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

I think I found it, will post soon.

…etch is fresh

ExternalApiService.get() caches responses for 20 minutes. getSeriesByTvdbId
and getMovieByTmdbId went through that cache, so the empty-show cleanup's
refetch — which runs immediately after unmonitor + episode-file deletes —
got served the pre-mutation snapshot and concluded the show still had
content. This is the actual root cause behind #2757 / #2891: every
gate-logic change was being fed stale input.

- getSeriesByTvdbId / getMovieByTmdbId now use getWithoutCache. These
  endpoints are read straight after mutations and also drive rule
  evaluation; both need current truth, not a cached snapshot.
- updateMovie's internal read-modify-write read is uncached too, so it
  no longer risks PUTting a stale movie object back to Radarr.
- Verified against Sonarr's debug log: the post-delete refetch issued no
  HTTP GET at all — it was a cache hit.
@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

Root cause — the 20-minute stale cache on getSeriesByTvdbId, with the debug-log evidence (no HTTP refetch was ever issued), and why it made every earlier gate-logic attempt moot.

Secondary fix — the allSeasonsUnmonitored gate jamming on never-downloaded TVDB seasons.
All 4 commits on the branch, attributed to what each does.

The hot-path note — getSeriesByTvdbId/getMovieByTmdbId are also used by rule getters, so the always-fresh trade-off is called out explicitly.

@enoch85

This comment was marked as resolved.

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

@daviddanko Ok, this should be it. I think it was cache. It's always cache or DNS you know... :)

Try the latest PR Release when it's built. 🙏🏼

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

Don't mind the failing check it's just a format error. Will fix pre merge.

@github-actions

Copy link
Copy Markdown
Contributor

Released to maintainerr/maintainerr:pr-2897 🚀

@daviddanko

Copy link
Copy Markdown

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

> Released to maintainerr/maintainerr:pr-2897 🚀

Am I good to go @enoch85?

Yes, good to go. 👍🏼

@daviddanko

Copy link
Copy Markdown

Looks good! Thanks for the hard work!

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

Looks good! Thanks for the hard work!

Great!!

Thanks for your testing and prompt answers. 🚀

@enoch85

enoch85 commented May 14, 2026

Copy link
Copy Markdown
Collaborator Author

@copilot please run yarn format fix to sort the failed test here.

Agent-Logs-Url: https://github.com/Maintainerr/Maintainerr/sessions/c0f6f3ce-7ed3-4b95-bbf6-3d5bf7f73c44

Co-authored-by: enoch85 <4511254+enoch85@users.noreply.github.com>

Copilot AI commented May 14, 2026

Copy link
Copy Markdown
Contributor

@copilot please run yarn format fix to sort the failed test here.

Done in 1d3c672.

@enoch85 enoch85 merged commit 1fd92cb into development May 14, 2026
13 checks passed
@enoch85 enoch85 deleted the fix/sonarr-delete-show-if-ended branch May 14, 2026 13:08
maintainerr-automation Bot added a commit that referenced this pull request May 14, 2026
* build(deps): bump the nestjs group with 4 updates (#2892)

Bumps the nestjs group with 4 updates: [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common), [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core), [@nestjs/platform-express](https://github.com/nestjs/nest/tree/HEAD/packages/platform-express) and [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing).


Updates `@nestjs/common` from 11.1.19 to 11.1.20
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.20/packages/common)

Updates `@nestjs/core` from 11.1.19 to 11.1.20
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.20/packages/core)

Updates `@nestjs/platform-express` from 11.1.19 to 11.1.20
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.20/packages/platform-express)

Updates `@nestjs/testing` from 11.1.19 to 11.1.20
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.20/packages/testing)

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.1.20
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.20
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nestjs
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump jest from 30.3.0 to 30.4.2 (#2893)

Bumps [jest](https://github.com/jestjs/jest/tree/HEAD/packages/jest) from 30.3.0 to 30.4.2.
- [Release notes](https://github.com/jestjs/jest/releases)
- [Changelog](https://github.com/jestjs/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jestjs/jest/commits/v30.4.2/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-version: 30.4.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump zod from 4.4.1 to 4.4.3 (#2894)

Bumps [zod](https://github.com/colinhacks/zod) from 4.4.1 to 4.4.3.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](colinhacks/zod@v4.4.1...v4.4.3)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.4.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump @tanstack/eslint-plugin-query (#2895)

Bumps [@tanstack/eslint-plugin-query](https://github.com/TanStack/query/tree/HEAD/packages/eslint-plugin-query) from 5.100.7 to 5.100.10.
- [Release notes](https://github.com/TanStack/query/releases)
- [Changelog](https://github.com/TanStack/query/blob/main/packages/eslint-plugin-query/CHANGELOG.md)
- [Commits](https://github.com/TanStack/query/commits/HEAD/packages/eslint-plugin-query)

---
updated-dependencies:
- dependency-name: "@tanstack/eslint-plugin-query"
  dependency-version: 5.100.10
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* ci(docs-drift): add open_pr input to assign Copilot for drafting docs PR

Adds an open_pr workflow_dispatch input. When enabled on a drift run, the
workflow appends drafting guidance (work from local clones, read full
files, source of truth is the listed commits, skip already-documented
items, doc-only minimal edits, structured PR description) to the drift
report before upserting the tracking issue on Maintainerr_docs, then
assigns Copilot to that issue so the coding agent drafts the PR.

* chore: wrap long lines in docs_drift.yml to satisfy yamllint (#2896)

The Copilot guidance bullets exceeded the .yamllint 160-character limit.
Wrapped each bullet onto continuation lines indented to align with the
list marker so the rendered markdown stays a single paragraph per bullet.

* chore: remove deprecated /api/plex legacy endpoints (#2884)

* fix(release): backfill missing new contributor

* docs(changelog): restore missing release headers

* fix(sonarr): don't require never-downloaded seasons to be unmonitored before show cleanup (#2897)

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: maintainerr-automation[bot] <261505141+maintainerr-automation[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: enoch85 <mailto@danielhansson.nu>
Co-authored-by: Anders Eriksson <36226327+blixten85@users.noreply.github.com>
@maintainerr-automation

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 3.11.2 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

doonga pushed a commit to greyrock-labs/home-ops that referenced this pull request May 14, 2026
…1.2 ) (#46)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/maintainerr/maintainerr](https://github.com/Maintainerr/Maintainerr) | patch | `3.11.1` → `3.11.2` |

---

### Release Notes

<details>
<summary>Maintainerr/Maintainerr (ghcr.io/maintainerr/maintainerr)</summary>

### [`v3.11.2`](https://github.com/Maintainerr/Maintainerr/releases/tag/v3.11.2)

[Compare Source](Maintainerr/Maintainerr@v3.11.1...v3.11.2)

##### Fixes

- Fixed an issue in Sonarr where the "Unmonitor and delete season + delete show if empty" action failed to delete the now-empty show after removing a season's files ([#&#8203;2897](Maintainerr/Maintainerr#2897)).

##### Internal

- Restored missing release headers in changelog.
- Backfilled missing new contributor in release notes.
- Added `open_pr` input to the `docs-drift` CI workflow for drafting documentation PRs.

##### Dependencies

- Updated 4 dependencies, including notable packages: [@&#8203;tanstack/eslint-plugin-query](https://github.com/tanstack/eslint-plugin-query), zod, and jest.

</details>

---

### Configuration

📅 **Schedule**: (in timezone America/New_York)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNyIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC43IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.greyrock.io/greyrock-labs/home-ops/pulls/46
enoch85 added a commit that referenced this pull request May 21, 2026
#2897 made getSeriesByTvdbId/getMovieByTmdbId uncached so the empty-show
cleanup reads post-deletion truth (#2757/#2891) — but that also de-cached
the rule-evaluation path, where the same series/movie resolves once per
item, so episode-level rules over large libraries regressed badly.

Add ArrLookupCache: a run-scoped memo created for the evaluation loop only
and never handed to the collection/action phase, so the cleanup still reads
fresh and 'delete then read stale cache' is structurally impossible. Only
the two uncached arr identity lookups use it; Tautulli/Seerr/Plex/Jellyfin/
Emby already cache at the API layer.
maintainerr-automation Bot added a commit that referenced this pull request May 22, 2026
* chore(server): switch jest transform to @swc/jest and make circular deps SWC-safe

ts-jest kept a full TypeScript program per worker, so workers were slow to
tear down under CI load and intermittently logged "A worker process has
failed to exit gracefully". @swc/jest is transpile-only: the server suite
drops from ~66s to ~13s and workers exit cleanly with no warning.

SWC's stricter CommonJS live-binding exports turn the codebase's existing
circular dependencies into TDZ ReferenceErrors at module load, so this makes
those cycles SWC-safe without changing runtime behaviour:

- Wrap singular concrete TypeORM relation property types in `Relation<>` so
  emitted `design:type` metadata no longer eagerly references the related
  entity class.
- Annotate forwardRef-injected constructor params with type-only import
  aliases so `design:paramtypes` doesn't eagerly reference the class; the
  value import stays for the forwardRef arrow. DI tokens are unchanged.
- Give the media-server factory's three adapter injections explicit
  `@Inject(forwardRef(...))` (matching the sibling params) so their types
  can be aliased as well.

Closes #2946

* Track collection media rule evaluation failures

* fix(server): correct collection ruleGroup entity type

* chore(server): remove legacy jest ts transforms

* fix(ui): provide production peer dependencies

* chore(server): format tsconfig.e2e.json

* build(deps): bump node from 26.1.0-alpine3.22 to 26.2.0-alpine3.22 (#2950)

Bumps node from 26.1.0-alpine3.22 to 26.2.0-alpine3.22.

---
updated-dependencies:
- dependency-name: node
  dependency-version: 26.2.0-alpine3.22
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump the nestjs group with 5 updates (#2951)

Bumps the nestjs group with 5 updates:

| Package | From | To |
| --- | --- | --- |
| [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common) | `11.1.21` | `11.1.22` |
| [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core) | `11.1.21` | `11.1.22` |
| [@nestjs/platform-express](https://github.com/nestjs/nest/tree/HEAD/packages/platform-express) | `11.1.21` | `11.1.22` |
| [@nestjs/swagger](https://github.com/nestjs/swagger) | `11.4.3` | `11.4.4` |
| [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing) | `11.1.21` | `11.1.22` |


Updates `@nestjs/common` from 11.1.21 to 11.1.22
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.22/packages/common)

Updates `@nestjs/core` from 11.1.21 to 11.1.22
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.22/packages/core)

Updates `@nestjs/platform-express` from 11.1.21 to 11.1.22
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.22/packages/platform-express)

Updates `@nestjs/swagger` from 11.4.3 to 11.4.4
- [Release notes](https://github.com/nestjs/swagger/releases)
- [Commits](nestjs/swagger@11.4.3...11.4.4)

Updates `@nestjs/testing` from 11.1.21 to 11.1.22
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.22/packages/testing)

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.1.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/swagger"
  dependency-version: 11.4.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.22
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nestjs
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump vite from 8.0.13 to 8.0.14 (#2952)

Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 8.0.13 to 8.0.14.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v8.0.14/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 8.0.14
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump @types/node from 22.19.17 to 22.19.19 (#2954)

Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 22.19.17 to 22.19.19.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 22.19.19
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump rolldown from 1.0.1 to 1.0.2 (#2953)

Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/rolldown/rolldown/releases)
- [Changelog](https://github.com/rolldown/rolldown/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rolldown/rolldown/commits/v1.0.2/packages/rolldown)

---
updated-dependencies:
- dependency-name: rolldown
  dependency-version: 1.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* refactor(server): decouple SettingsService into a passive data store + coordinator

Introduce SettingsStoreService as the sole owner of persisted settings state
(the hydrated fields, init(), reads, public masking, and low-level
persistence), depending only on the settings repositories. SettingsService
becomes a pure coordinator (test/save/reinit flows) with no settings state of
its own, and every read consumer injects the store.

Because the API services and media-server adapters/factory now read settings
from the store instead of SettingsService, the circular dependencies are gone:
constructor forwardRef injections drop from 28 to 2 and the @swc/jest type-only
import aliases from 28 to 2 -- the only remaining pair is the genuine, mutual
MediaServerFactory <-> MediaServerSwitchService cycle (factory checks
isSwitching(); switch calls uninitializeServer()).

No behaviour change: SettingsService keeps its public API by delegating reads to
the store; save flows persist via the store and re-hydrate. Public settings
masking, save/test/connection, and media-server switch flows are preserved.

* refactor(server): rename settings services (and the fields holding them) to reflect roles

SettingsStoreService -> SettingsDataService (owns/reads/persists settings data)
SettingsService      -> SettingsOperationsService (tests connections + applies changes)

Renames the classes, files, import paths, and the injected fields/mocks that hold
them so each field's name matches the service it holds. Also points AppModule at
SettingsDataService for init() (hydration is a data concern), and keeps the
deliberate names where they read better (`appSettings` in notification agents,
`settings` for plain data reads, the spec SUT `service`). No behaviour change.

* fix: overlay notification titles for Emby/Jellyfin movies and UI input warnings

- notifications: render media items by item type so Emby/Jellyfin movies
  (whose parentId points at the library folder) no longer render as
  "undefined - season undefined"
- emby: log the server response body when a collection image upload fails,
  so a 500 is diagnosable instead of a bare status
- rule builder: keep the Custom Value inputs controlled (coalesce undefined)
- router: add a HydrateFallback element to silence the initial-hydration warning

* chore: type-check on every test run

Add a server check-types script and make the turbo test task depend on it, so
SWC-transpiled tests (which skip type-checking) can no longer hide type errors.
Remove stale testConnections mocks this surfaced in the collection-worker spec.

* perf: parallelize per-item Plex watch-history reads during rule execution

* refactor: address review — single batching layer, honest knob name, per-item abort

- Collapse to one bounded-parallel layer in the comparator and revert the
  Plex-getter internal episode batching, so total in-flight lookups never
  exceed the cap (was up to cap x cap when nested).
- Rename WATCH_HISTORY_CONCURRENCY -> RULE_EVALUATION_CONCURRENCY: the pool
  bounds per-item operand evaluation across all getters, not just Plex.
- Restore abort checks inside the batched tasks for prompt cancellation.

* perf: batch rule operand reads with bounded concurrency

* perf(rules): dedupe uncached Sonarr/Radarr identity lookups per run

#2897 made getSeriesByTvdbId/getMovieByTmdbId uncached so the empty-show
cleanup reads post-deletion truth (#2757/#2891) — but that also de-cached
the rule-evaluation path, where the same series/movie resolves once per
item, so episode-level rules over large libraries regressed badly.

Add ArrLookupCache: a run-scoped memo created for the evaluation loop only
and never handed to the collection/action phase, so the cleanup still reads
fresh and 'delete then read stale cache' is structurally impossible. Only
the two uncached arr identity lookups use it; Tautulli/Seerr/Plex/Jellyfin/
Emby already cache at the API layer.

* Handle rule evaluation failures for manual collection media

* test(rules): cover ArrLookupCache dedupe and evict-on-failure

* test(actions): assert empty-show cleanup reads series fresh, never via a memo

Guards the integration invariant behind the rule-evaluation ArrLookupCache:
the cleanup path resolves the series from the uncached Sonarr client on every
run. If a stale/memoized value ever leaked into this path, the second run
(files now gone) would skip deletion — this test would fail.

* refactor(server): remove module forwardRefs left by the settings split

#2955 split the SettingsService god-object and made SettingsModule a @global
provider of SettingsDataService, but left the module-level forwardRef wiring in
place. With the settings cycles gone, those forwardRefs are now removable.

- Drop the vestigial `forwardRef(() => SettingsModule)` import from the Plex,
  TMDB, TVDB, Jellyfin and Emby modules: each only reads settings via the
  @global SettingsDataService, so it needs no import at all.
- Convert SettingsModule's nine `forwardRef(() => XApiModule)` imports to plain
  imports now that no API module imports SettingsModule back.
- Break the last MediaServerFactory <-> MediaServerSwitchService cycle (the pair
  #2955 called irreducible) by extracting the in-progress flag into a zero-
  dependency MediaServerSwitchState holder: the factory reads it, the switch
  service writes it, neither depends on the other. This removes the final two
  constructor forwardRefs and their two @swc/jest type-only aliases.
- Move the data-only testSetup() onto SettingsDataService (kept as a delegating
  wrapper on SettingsOperationsService) so MediaServerSetupGuard reads it from
  the @global service, letting MediaServerModule drop its SettingsModule import.

forwardRef constructor injections and type-only aliases both drop to 0. The
only remaining forwardRefs are the genuine, pre-existing CollectionsModule <->
RulesModule domain cycle, untouched here.

No behaviour change.

* chore: pre-approve project MCP servers (github, playwright)

Project-scoped .mcp.json servers require per-project approval before Claude Code
loads them; without it the playwright server silently never attaches and its
browser tools are unavailable. Add enabledMcpjsonServers so both configured
servers auto-connect on session start.

* build(deps): bump the nestjs group with 4 updates (#2960)

Bumps the nestjs group with 4 updates: [@nestjs/common](https://github.com/nestjs/nest/tree/HEAD/packages/common), [@nestjs/core](https://github.com/nestjs/nest/tree/HEAD/packages/core), [@nestjs/platform-express](https://github.com/nestjs/nest/tree/HEAD/packages/platform-express) and [@nestjs/testing](https://github.com/nestjs/nest/tree/HEAD/packages/testing).


Updates `@nestjs/common` from 11.1.22 to 11.1.23
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.23/packages/common)

Updates `@nestjs/core` from 11.1.22 to 11.1.23
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.23/packages/core)

Updates `@nestjs/platform-express` from 11.1.22 to 11.1.23
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.23/packages/platform-express)

Updates `@nestjs/testing` from 11.1.22 to 11.1.23
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.23/packages/testing)

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.1.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.23
  dependency-type: direct:development
  update-type: version-update:semver-patch
  dependency-group: nestjs
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump typescript-eslint from 8.59.3 to 8.59.4 (#2961)

Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.59.3 to 8.59.4.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.4/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.59.4
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps): bump @hookform/resolvers from 5.2.2 to 5.4.0 (#2962)

Bumps [@hookform/resolvers](https://github.com/react-hook-form/resolvers) from 5.2.2 to 5.4.0.
- [Release notes](https://github.com/react-hook-form/resolvers/releases)
- [Commits](react-hook-form/resolvers@v5.2.2...v5.4.0)

---
updated-dependencies:
- dependency-name: "@hookform/resolvers"
  dependency-version: 5.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* build(deps-dev): bump postcss from 8.5.14 to 8.5.15 (#2963)

Bumps [postcss](https://github.com/postcss/postcss) from 8.5.14 to 8.5.15.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](postcss/postcss@8.5.14...8.5.15)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.15
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* perf(rules): lower RULE_EVALUATION_CONCURRENCY 16 -> 8

16 over-drove co-located CPU-heavy backends (Tautulli history queries on an
all-in-one N100), pushing requests past the 10s timeout and triggering the
retry feedback loop. 8 keeps concurrent load in check while staying far
faster than the old sequential path.

* fix(collections): address review feedback on rule-evaluation failure tracking

- collection-worker.server.spec: drop mock for non-existent
  SettingsDataService.testConnections (failed check-types CI)
- collection-worker: keep original mediaToHandle naming, apply the
  ruleEvaluationFailed filter inline (per review feedback)
- collections.service: make setCollectionMediaRuleEvaluationFailed
  best-effort so a failed flag write can't abort the rule run

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: enoch85 <mailto@danielhansson.nu>
Co-authored-by: Kristian Matthews-Kennington <kristian@matthews-kennington.com>
Co-authored-by: maintainerr-automation[bot] <261505141+maintainerr-automation[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
@enoch85 enoch85 added this to the 3.11.2 milestone May 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

3 participants