Skip to content

chore: sync development to main#2979

Merged
maintainerr-automation[bot] merged 41 commits into
mainfrom
development
May 27, 2026
Merged

chore: sync development to main#2979
maintainerr-automation[bot] merged 41 commits into
mainfrom
development

Conversation

@maintainerr-automation

@maintainerr-automation maintainerr-automation Bot commented May 25, 2026

Copy link
Copy Markdown
Contributor

Summary

Promotes development to main for release. Squash-merge when approved; release automation continues on approval.

Changes

maintainerr-automation Bot and others added 16 commits May 23, 2026 21:27
- CSS-first theme: @theme + @plugin in globals.css; remove tailwind.config.js
- Use @tailwindcss/vite; drop postcss, autoprefixer, @tailwindcss/aspect-ratio
- Fix v4 regressions: @tailwindcss/forms .form-input collision (strategy: base),
  dead bg-opacity-* -> slash syntax, checkbox focus ring + single .checkbox class,
  translucent backgrounds, unlayered prose colors
- Standardize Plex checkboxes, Manual badge, and input-action button states;
  delay the overlay-schedule loading hint
- Add dev/seed-db.mjs to seed collections/rules/storage for local testing
- color-scheme: dark so native controls render dark
- container query for the media-card grid (sizes to container, not viewport)
- field-sizing-content on the rule description and overlay text textareas
- text-shadow on poster-overlaid title/year/summary
- drop the v3->v4 border-color compat shim (one implicit border made explicit)

Closes #2970
- Move the field base style into the global input/select/textarea rule so it
  is the single source of truth; Forms/Input and Forms/Select keep deltas only
  (adornments, join, error, select chevron). One edit now restyles every field.
- SearchBar uses the shared field style (drops the pill override; fixes the
  clipped icon and the blue focus ring).
- Include input[type=search] in the shared rule; maintainerr focus app-wide.
- Keep the v4 field-sizing auto-grow on the rule description + overlay text,
  with min-h so the textareas no longer collapse when empty.
- Uniform rule-form row spacing; blank the log filter's default option (was '-').
- ExternalApiService: skip caching Buffer/null/non-object responses, so binary
  blobs and empty bodies can't poison the shared NodeCaches.
- cache: exempt tmdb/tvdb (immutable external metadata, 6h TTL) from the per-run
  flushAll(), so rule runs don't needlessly re-fetch them.
- tmdb metadata provider: ignore non-object records.
…ge classification

- Pass NEXT_RELEASE_VERSION/LAST_RELEASE_GITTAG/NEXT_RELEASE_GITHEAD into
  the notes generator via generateNotesCmd. semantic-release does not export
  these to plugin commands, so buildHeader() always saw an empty version and
  emitted no '# [x.y.z](compare) (date)' header for v3.11.2..v3.12.1.
- Tell the model to net out the commit range: an identifier introduced and
  renamed/removed within one release never shipped, so it is not breaking.
- Backfill the missing v3.11.2/v3.12.0/v3.12.1 headers and drop the false
  'Breaking Changes' (an internal settings-service rename) from v3.12.1.
…ercion (#2971)

* fix: treat unset rule section operator as OR instead of AND

An unset section operator (null) was coerced to AND by `+null === 0`, so
sections meant to OR together were evaluated as AND. The coercion is now
null-guarded, resolving an unset operator to OR — consistent with how a
null operator is already treated within a section.

Also require an explicit operator on non-first rules in the rule editor,
backfill existing unset operators to OR, and stop dropping a numeric AND
section operator on YAML export.

Co-Authored-By: James Nobes <github@stormshaker.com>

* fix(rules): normalize legacy section operators

---------

Co-authored-by: enoch85 <mailto@danielhansson.nu>
Co-authored-by: Kristian Matthews-Kennington <kristian@matthews-kennington.com>
…2976)

Imports could silently break when a rule's properties don't all map to
the configured media server:

- Migrate firstVal and lastVal independently by each field's own app, so a
  media-server property compared against a Seerr/Tautulli value is no longer
  stranded on the source server (Plex/Emby) and rendered blank. Covers
  Plex/Jellyfin/Emby.
- Surface rules dropped on import (no equivalent on the target server) via a
  toast on both the community and YAML paths; keep the warn log.
- YAML export no longer writes `App.undefined` for an unknown property, and
  import skips an unresolved rule (reported as a skipped count) instead of
  rejecting the whole document.
- validateRule returns a clean status instead of throwing on a missing
  application/property; rule-group create no longer fails when notifications
  is omitted.
Dev-only HTTP stub answering the Jellyfin endpoints the server's adapter
calls (connection, users, libraries, item metadata, item images). Pairs
with dev/seed-db.mjs so the editor library list, rule-group save,
collection grids and rule evaluation work without a real Jellyfin.
Invented data only; not referenced by application code.
)

Adds a show/season rule property (sw_markedWatchedEpisodes) backed by
Plex's viewedLeafCount (watched state) instead of play history.

Unlike "Amount of watched episodes" (sw_viewedEpisodes), which counts
episodes that have a play-history entry, this counts episodes Plex
considers watched -- including those manually marked as watched, since
Plex sets viewedLeafCount for manual marks but writes no play history.
Co-authored-by: enoch85 <mailto@danielhansson.nu>
…2977)

Adds a Jellyfin-only Streamystats rule Application (the Jellyfin analog of
Tautulli) with two watchlist-backed properties: "Is in a watchlist" and
"[list] In watchlist of (username)". Upstream Streamystats now accepts
Jellyfin API-key auth on its watchlist endpoints, so Maintainerr can reach
them via the MediaBrowser token scheme.

Only public Streamystats watchlists are visible to the API key. The
membership snapshot is cached in the shared Streamystats NodeCache (flushed
between rule-group runs). The Application is removed from the rule constants
unless Streamystats is configured, and the UI hides it on non-Jellyfin
servers. Username resolution failures surface as a transient skip rather
than an empty list.
@maintainerr-automation maintainerr-automation Bot requested a review from enoch85 as a code owner May 25, 2026 15:39
@maintainerr-automation maintainerr-automation Bot added the release:docker-build Build release candidate Docker image label May 25, 2026
enoch85 and others added 9 commits May 25, 2026 22:24
…#2980)

- AGENTS.md is the single documentation index; the Claude, Copilot, Cursor,
  and Codex entrypoints route to it and name the standing rules directly so
  they can't be missed.
- Split standing rules (read every session) from task-specific docs (read on
  demand); scope release-review's Copilot applyTo to release artifacts so it
  no longer loads on every interaction.
- Add project-notes.instructions.md (non-obvious project knowledge and
  conventions for handoff) and README_AGENTS.md (the wiring map).
- Move dev mocks + DB seed to tools/dev/ (fix seed-db repo-root resolution);
  add the seeded-DB + Playwright step to the release-review checklist.
- implementation rule 8: demand TypeORM + typeorm_instructions.txt workflow,
  require migrations be tested end-to-end before considered working
- release-review: fix broken typeorm_instructions.txt path (root, not
  .github/instructions/); clarify that generated schema migrations may contain
  raw DDL while hand-written data migrations must use QueryBuilder
…t notes

How to exercise both import paths (YAML encode/decode vs community-rule
/migrate), why they don't share code, and quick checks for the #2971/#2976
behaviours and v2.0.0+ community-rule import.
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.23 to 11.1.24
- [Release notes](https://github.com/nestjs/nest/releases)
- [Commits](https://github.com/nestjs/nest/commits/v11.1.24/packages/common)

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

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

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

---
updated-dependencies:
- dependency-name: "@nestjs/common"
  dependency-version: 11.1.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/core"
  dependency-version: 11.1.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/platform-express"
  dependency-version: 11.1.24
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: nestjs
- dependency-name: "@nestjs/testing"
  dependency-version: 11.1.24
  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>
Bumps [date-fns](https://github.com/date-fns/date-fns) from 4.2.1 to 4.3.0.
- [Release notes](https://github.com/date-fns/date-fns/releases)
- [Commits](date-fns/date-fns@v4.2.1...v4.3.0)

---
updated-dependencies:
- dependency-name: date-fns
  dependency-version: 4.3.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>
Bumps [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) from 8.59.4 to 8.60.0.
- [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.60.0/packages/typescript-eslint)

---
updated-dependencies:
- dependency-name: typescript-eslint
  dependency-version: 8.60.0
  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>
Bumps [@swc/core](https://github.com/swc-project/swc/tree/HEAD/packages/core) from 1.15.33 to 1.15.40.
- [Release notes](https://github.com/swc-project/swc/releases)
- [Changelog](https://github.com/swc-project/swc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/swc-project/swc/commits/v1.15.40/packages/core)

---
updated-dependencies:
- dependency-name: "@swc/core"
  dependency-version: 1.15.40
  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>
Bumps [knip](https://github.com/webpro-nl/knip/tree/HEAD/packages/knip) from 6.14.1 to 6.14.2.
- [Release notes](https://github.com/webpro-nl/knip/releases)
- [Commits](https://github.com/webpro-nl/knip/commits/knip@6.14.2/packages/knip)

---
updated-dependencies:
- dependency-name: knip
  dependency-version: 6.14.2
  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>
…ort operator backfill) (#2986)

* fix(rules): resolve TEXT_LIST custom value on YAML import

The encoder serialises a custom value's type as the RuleType humanName, so
TEXT_LIST becomes "text list" (with a space). The decoder's switch only matched
'TEXT_LIST', so the spaced key fell through, left ruleType undefined, and threw
on the return's .toString() — failing the entire YAML import. Normalise spaces
to underscores before matching.

* style: run yarn format on rules constants spec

Agent-Logs-Url: https://github.com/Maintainerr/Maintainerr/sessions/38e8bea3-daf7-4de0-aeac-a61c2d3e5fa2

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

* fix(rules): backfill unset within-section operator on community import

migrateImportedRuleDtos remapped apps and reasserted section-boundary operators
but left within-section unset operators as null. A pre-explicit-operator
community rule (e.g. one authored before #2971) could therefore carry a null
operator on a non-first rule, which the new "operator is required for every rule
after the first" save validation rejects on import. Backfill those to OR after
the boundary pass — the same default the comparator and the
NormalizeRuleSectionOperators migration apply.

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
@enoch85

enoch85 commented May 25, 2026

Copy link
Copy Markdown
Collaborator

Release testing — end-to-end results (v3.12.1..development)

Seeded dev DB + Playwright, against both the Jellyfin and Plex mock stacks.

What we tested Result (observed) How
Migration on boot (#2971) NormalizeRuleSectionOperators… in migrations table booted on real SQLite; queried table
Rule engine (/api/rules/test, grp 337 × 3 movies) code:1, 26 rules each; ratings 9/6/2, Times viewed 0, in 3 collections, 1920x1080/h264, Is Watched false curl + JSON parse vs Jellyfin mock
Null / unary path (#2978) two date fields null + firstValueReason:"…not recorded", no crash /api/rules/test JSON
OR section combining (#2971) first rule no operator; 25 rules operator:OR; section true rules/test JSON + Test Media YAML
YAML export — AND preserved (#2971) operator: AND present in encoded YAML /api/rules/yaml/encode of AND-boundary group
YAML export — no App.undefined (#2976) absent; DATE/NUMBER round-trip encode→decode (code 1) encode→decode round-trip
YAML import — unresolved prop SKIP (#2976) code:1, skipped:1, 2 of 3 rules kept decode YAML with one bogus firstValue
Cross-server /migrate (#2976) Plex [0,0][6,0]; Seerr [3,0] untouched while lastVal[0,5][6,5] (independent remap) POST /api/rules/migrate
Community rules import — ALL versions 165/165 migrate code:1, 0 failures; 0 residual non-first null operators /api/rules/community/migrate each
Community import — full UI flow (rule 81) imported via Community modal → values populate → operators [null, AND, OR]saved, 3 rules, Active Playwright (import → select Radarr server → Save)
Plex watched-episodes rule (#2975) "Amount of episodes marked as watched" = 1 (viewedLeafCount:1), result true Plex stack; /api/rules/test grp 341 × show sh1
Collections page (Tailwind v4) 3 cards, grid hydrated 14/10/8, sizes/schedules, Active Playwright DOM assert + screenshot
Rules page + editor 3 group cards; editor opens /rules/edit/337 Playwright DOM assert
Test Media search (#2978) styled field; typeahead → [Mock Alpha/Bravo/Charlie]; Test → YAML with operator:OR Playwright type/select/Test + screenshot
Storage API 200, reachable:true, Radarr+Sonarr ok, lib sizes; totals 0 curl + Playwright
Browser console only benign /api/collections/337/poster 404 Playwright console capture
TEXT_LIST custom value YAML import (fixed, PR #2986) was code:0 crash → now code:1, customVal preserved live round-trip + unit tests
Within-section null operator on community import (fixed, PR #2986) rule 81 would fail #2971 save-gate → now [null, AND, OR], saves live + unit test + full UI save

Suites: full repo test suite green · check-types · format:check · build — all clean.
Not driven: Streamystats getter (needs live service), cache hygiene #2972 (unit-tested), release-notes tooling (release workflow).

Verdict: ✅ no CRITICAL/HIGH/MEDIUM regressions.

dependabot Bot and others added 2 commits May 26, 2026 20:15
* build(deps): bump typeorm from 0.3.29 to 1.0.0

Bumps [typeorm](https://github.com/typeorm/typeorm) from 0.3.29 to 1.0.0.
- [Release notes](https://github.com/typeorm/typeorm/releases)
- [Changelog](https://github.com/typeorm/typeorm/blob/master/CHANGELOG.md)
- [Commits](typeorm/typeorm@0.3.29...1.0.0)

---
updated-dependencies:
- dependency-name: typeorm
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(server): use object-form find relations for typeorm 1.0.0

TypeORM 1.0.0 removed string-based relations from find methods (#12215).
Convert the six affected findOne/find calls to the object form, which is
valid in both 0.3.x and 1.0.0.

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: enoch85 <mailto@danielhansson.nu>
Bumps [@tanstack/eslint-plugin-query](https://github.com/TanStack/query/tree/HEAD/packages/eslint-plugin-query) from 5.100.11 to 5.100.14.
- [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/@tanstack/eslint-plugin-query@5.100.14/packages/eslint-plugin-query)

---
updated-dependencies:
- dependency-name: "@tanstack/eslint-plugin-query"
  dependency-version: 5.100.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>
dependabot Bot and others added 11 commits May 26, 2026 21:49
Bumps [react-hook-form](https://github.com/react-hook-form/react-hook-form) from 7.76.0 to 7.76.1.
- [Release notes](https://github.com/react-hook-form/react-hook-form/releases)
- [Changelog](https://github.com/react-hook-form/react-hook-form/blob/master/CHANGELOG.md)
- [Commits](react-hook-form/react-hook-form@v7.76.0...v7.76.1)

---
updated-dependencies:
- dependency-name: react-hook-form
  dependency-version: 7.76.1
  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>
Bumps [nodemailer](https://github.com/nodemailer/nodemailer) from 8.0.8 to 8.0.9.
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](nodemailer/nodemailer@v8.0.8...v8.0.9)

---
updated-dependencies:
- dependency-name: nodemailer
  dependency-version: 8.0.9
  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>
)

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

---
updated-dependencies:
- dependency-name: "@tanstack/react-query"
  dependency-version: 5.100.14
  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>
Discord rejects webhook payloads with 400 Invalid Form Body when an
embed contains an empty thumbnail object. Imageless notifications (e.g.
media-removed / rule-handling events) always sent thumbnail.url=undefined,
which serialized to an empty thumbnail and silently failed. Only attach
the thumbnail when an image is present.
Bumps [turbo](https://github.com/vercel/turborepo) from 2.9.14 to 2.9.15.
- [Release notes](https://github.com/vercel/turborepo/releases)
- [Changelog](https://github.com/vercel/turborepo/blob/main/RELEASE.md)
- [Commits](vercel/turborepo@v2.9.14...v2.9.15)

---
updated-dependencies:
- dependency-name: turbo
  dependency-version: 2.9.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>
…#2998)

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

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-version: 8.60.0
  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>
Bumps [rolldown](https://github.com/rolldown/rolldown/tree/HEAD/packages/rolldown) from 1.0.2 to 1.0.3.
- [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.3/packages/rolldown)

---
updated-dependencies:
- dependency-name: rolldown
  dependency-version: 1.0.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>
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 8.59.4 to 8.60.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.60.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-version: 8.60.0
  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>
…P 414) (#3001)

Creating a new collection from a large rule seeded every item id into the
create request's query string (BULK_COLLECTION_CREATE, #2911), overflowing
the URL and failing with "Failed to create collection" / HTTP 414 URI Too
Long. Item ids can only travel in the query string on Plex/Jellyfin/Emby
(no request body), so the seed is unbounded.

Revert to the pre-#2911 / 2.x behaviour: create the collection empty and
populate it via the existing batched add path (addBatchToCollection), for
all media servers. Removes the now-unused BULK_COLLECTION_CREATE capability
and the initialItemIds plumbing.
…2991)

* fix(rules): scope exclusions to their rule group under TypeORM 1.0

Adopt TypeORM 1.0 where-clause null semantics (default throw; IsNull() to
match NULL) instead of 0.3.x's silent-ignore footgun, fixing latent exclusion
bugs surfaced by the typeorm 1.0.0 bump (#2983):

- getExclusions: a group-scoped exclusion no longer leaks into other rule
  groups (and no longer returns duplicates); global exclusions still apply
  everywhere.
- setExclusion: global exclusions subsume scoped ones — an item is global or
  scoped, never both.
- removeExclusion: removing a global exclusion no longer writes a spurious
  collection-log entry; logs media + ownership.
- getCollection: a null/blank id returns undefined instead of an arbitrary
  first collection.

Bumps engines.node to TypeORM 1.0's floor and adds getter-property and
exclusion-scoping regression coverage.

* feat(ui): warn before a global exclusion replaces rule-group exclusions

When adding a global exclusion for an item that already has rule-group
exclusions, show a confirmation listing each "<item> — <rule group>" (the
group links to its collection, reusing the backdrop's maintainerr-status data
so labels/links match and stay fresh) before proceeding, since going global
removes those scoped exclusions. Only triggers on the Add action, never on
Remove.

* test(ui): cover the global-exclusion warning in AddModal

* fix(ui): clarify Add/Remove action labels in the media modal

* fix(ui): don't block global exclusion when warning prefetch fails
* fix(plex): don't drop auth when plex.tv is unreachable

A plex.tv timeout was reported as an invalid token, so the Plex settings
page declared stored credentials invalid and demanded re-authentication
even when the saved token was fine. Distinguish a genuine rejection
(401/403) from a connectivity failure: on unreachable, keep the account
authenticated, warn, and auto-retry until plex.tv responds.

* fix(ui): clear stale Plex auth warning

enoch85 commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Release testing — addendum (post-#2986 commits, development tip f64a0c80)

Complements the earlier results table, which was posted before five substantive commits landed (#2983 TypeORM 1.0, Discord thumbnail, #3001, #2991, #2996). Seeded dev DB (Plex) + live server boot on TypeORM 1.0; exclusion/auth flows driven via the REST API, collection create via a real rule run.

What we tested Result (observed) How
TypeORM 0.3.29→1.0.0 boot (#2983) server boots clean; migrations table carries NormalizeRuleSectionOperators1779622081794; IsNull()/Not(IsNull())/delete/findOne all execute live (the whole exclusion suite below runs on 1.0) booted real SQLite; GET /api/settings 200
Exclusion scoping — no cross-group leak (#2991) scoped excl. on grp 183 absent from GET ?rulegroupId=184; present in 183; the global is appended exactly once (no dup) POST /api/rules/exclusion (collectionId 16) + GET per group
Global subsumes scoped (#2991) adding a global for 90001 deleted the scoped grp-183 row and left one global row (ruleGroupId NULL) — no duplicate POST scoped → POST global; queried exclusion table
Already-global → scoped add skipped (#2991) re-adding scoped (grp 184) for a globally-excluded item is a no-op; log: Media 90001 is already globally excluded; skipped rule group 184 exclusion POST scoped while global exists; DB unchanged
Remove global exclusion (#2991) code:1; log Removed exclusion 33 for media 90001 (global); no spurious collection-log; no TypeORM-1.0 null-where throw (the 0.3.x footgun) DELETE /api/rules/exclusion/33
Collection create — no ids / no 414 (#3001) live rule run created collection 95000; create URI is /library/collections?type=&title=&sectionId=zero item ids in the query string POST /api/rules/185/execute + log + source
Plex auth — invalid token (#2996) plex.tv reachable + bad seed token → {status:NOK, "Stored Plex credentials are invalid…"} — classified invalid, not unreachable GET /api/settings/test/plex/auth
AddModal global-exclusion warning (#2991 UI) confirm modal lists each "item — rule group" before going global; best-effort fall-through if prefetch fails Vitest spec (4 cases)
Discord empty thumbnail thumbnail omitted from embed when payload.image is empty Vitest spec

Suites: full repo test suite green · check-types · build — all clean.
Not driven: Plex auth unreachable branch (can't sever plex.tv locally; unit-tested in plextvApi.spec.ts) · Discord live webhook delivery (unit-tested) · collection batch-add populate step (blocked by un-mockable plex.tv hub-visibility enrichment, the documented local-mock limitation; adapter unit-tested).

Verdict: ✅ no CRITICAL/HIGH/MEDIUM regressions in the post-comment changes.


Follow-up validation — review fix + upgrade & full-rule regression

Performed while finalizing review fixes; captured as two permanent regression harnesses.

What we tested Result (observed) How
Review fix — global-exclusion warning links now SPA navigate() (respects router basename; old <a href> 404'd under BASE_PATH) + status-cache clear & ['collections'] invalidation so the destination still loads fresh unit spec (5 cases) + manual UI on the seeded stack
1.x → current upgrade path reconstructed the v1.7.1 schema (6 migrations), seeded 1.x-era rules, then applied all 37 post-1.7.1 migrations in order (ending NormalizeRuleSectionOperators); operator backfill correct, seeded rows survived every SQLite table rebuild, late 3.x columns present new upgrade-from-1x.spec.ts (real TypeORM migration runner on a temp SQLite)
Migration class-name stability the 6 v1.7.1 migration class names are byte-identical to current → TypeORM skips them on upgrade (no re-run / "table exists") v1.7.1 vs HEAD source compare
Rule catalog vs v3.12.1 8 apps / 208 props vs 7 / 205: +3 (Plex sw_markedWatchedEpisodes #2975; Streamystats isInWatchlist + watchlistedByUsers #2977), 0 removed, 0 changed#2922 getter refactor caused zero catalog drift dump every Application×Property on both builds, diff
Comparator matrix vs v3.12.1 — all RuleType×RulePossibility (107 scenarios) 106/107 identical; the sole diff is the intended #2971 fix (AND/false → OR/true). Unary EXISTS/NOT_EXISTS identical on both (no crash) expanded rules-test-matrix.e2e.ts run on both builds, diff

Suites: full repo yarn turbo test green with the two new regression specs added; check-types, eslint, prettier clean.

Verdict: ✅ upgrade path applies cleanly from 1.x; no rule-evaluation regression vs v3.12.1 beyond the intended #2971 change.

enoch85 added 3 commits May 27, 2026 22:47
The rule-group links in the AddModal global-exclusion warning used a raw
<a href>, which forced a full page reload and ignored the router basename
(404 under BASE_PATH subpath installs). Use useNavigate instead, and clear
the maintainerr-status cache + invalidate collection queries first so the
destination still fetches fresh — what the full reload did implicitly.
…n coverage

- upgrade-from-1x.spec.ts: reconstructs the v1.7.1 schema, seeds 1.x-era
  data (rules with null operators), applies every post-1.7.1 migration and
  asserts the operator backfill + late-migration schema survive.
- rules-test-matrix.e2e.ts: generated matrix covering every RuleType x
  RulePossibility (value/missing/present/absent), for cross-version diffing.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved by release automation (CODEOWNER approval was verified by Release 2).

@maintainerr-automation maintainerr-automation Bot merged commit ea4a249 into main May 27, 2026
25 checks passed
@maintainerr-automation

Copy link
Copy Markdown
Contributor Author

📚 Docs drift report

Comparing origin/mainHEAD against Maintainerr_docs. Informational only — maintainers decide what needs doc updates before release.

Rule glossary parity

  • Code rule keys (rules.constants.ts): 163
  • Documented keys (docs/Glossary.md): 161
In code but missing from Glossary (2)
  • Streamystats.isInWatchlist
  • Streamystats.watchlistedByUsers

New migrations on this branch

  • 1779622081794-NormalizeRuleSectionOperators.ts

Each migration typically introduces a setting or schema change. Confirm Configuration.md and Migration.md still reflect user-facing behaviour.

Rule constants

  • rules.constants.ts changed: +37 / -0 lines
  • Review rule tables in docs/Rules.mdx and entries in docs/Glossary.md.

Public contracts (@maintainerr/contracts)

  • Added (1):
    • packages/contracts/src/streamystats/watchlists.ts
  • Modified (5):
    • packages/contracts/src/media-server/enums.ts
    • packages/contracts/src/media-server/features.ts
    • packages/contracts/src/media-server/types.ts
    • packages/contracts/src/rules/constants.ts
    • packages/contracts/src/streamystats/index.ts

Public DTO changes may affect docs/API.md and the OpenAPI spec in static/openapi-spec/maintainerr_api_specs.yaml.

New HTTP controllers

No new controllers.

feat: commits on this branch

Behavioral fixes worth reviewing

  • a6caec2 fix(ui): navigate client-side from global-exclusion warning links
    • touched: apps/ui/src/components/AddModal/index.spec.tsx, apps/ui/src/components/AddModal/index.tsx
  • f64a0c8 fix(plex): don't drop auth when plex.tv is unreachable (fix(plex): don't drop auth when plex.tv is unreachable #2996)
    • touched: apps/server/src/modules/api/lib/plextvApi.spec.ts, apps/server/src/modules/api/lib/plextvApi.ts, apps/server/src/modules/api/plex-api/plex-api.service.ts, apps/server/src/modules/settings/settings-operations.service.spec.ts, apps/server/src/modules/settings/settings-operations.service.ts, apps/ui/src/api/settings.ts, apps/ui/src/components/Settings/Plex/index.spec.tsx, apps/ui/src/components/Settings/Plex/index.tsx
  • 787e30d fix(rules): scope exclusions to their rule group under TypeORM 1.0 (fix(rules): scope exclusions to their rule group under TypeORM 1.0 #2991)
    • touched: apps/server/src/modules/collections/collections.service.ts, apps/server/src/modules/notifications/notifications.service.ts, apps/server/src/modules/rules/getter/getter-property-coverage.spec.ts, apps/server/src/modules/rules/rules.service.exclusion-scoping.integration.spec.ts, apps/server/src/modules/rules/rules.service.exclusions.spec.ts, apps/server/src/modules/rules/rules.service.ts, apps/ui/src/components/AddModal/index.spec.tsx, apps/ui/src/components/AddModal/index.tsx
  • 9f7271e fix(collections): create collections empty, then batch-add (avoid HTTP 414) (fix(collections): create collections empty, then batch-add (avoid HTTP 414) #3001)
    • touched: apps/server/src/modules/api/media-server/emby/emby-adapter.service.spec.ts, apps/server/src/modules/api/media-server/emby/emby-adapter.service.ts, apps/server/src/modules/api/media-server/jellyfin/jellyfin-adapter.service.spec.ts, apps/server/src/modules/api/media-server/jellyfin/jellyfin-adapter.service.ts, apps/server/src/modules/api/media-server/plex/plex-adapter.service.spec.ts, apps/server/src/modules/api/media-server/plex/plex-adapter.service.ts, apps/server/src/modules/api/plex-api/interfaces/collection.interface.ts, apps/server/src/modules/api/plex-api/plex-api.service.ts, apps/server/src/modules/collections/collections.service.spec.ts, apps/server/src/modules/collections/collections.service.ts
  • 950e66d fix(notifications): omit empty Discord embed thumbnail
    • touched: apps/server/src/modules/notifications/agents/discord.spec.ts, apps/server/src/modules/notifications/agents/discord.ts
  • c545c15 fix(rules): harden rule import (TEXT_LIST YAML decode + community-import operator backfill) (fix(rules): harden rule import (TEXT_LIST YAML decode + community-import operator backfill) #2986)
    • touched: apps/server/src/modules/rules/constants/constants.service.spec.ts, apps/server/src/modules/rules/constants/constants.service.ts, apps/server/src/modules/settings/rule-migration.service.spec.ts, apps/server/src/modules/settings/rule-migration.service.ts
  • 6ba3040 fix: Test Media search styling (Tailwind v4) + unary-rule comparison crash (fix: Test Media search styling (Tailwind v4) + unary-rule comparison crash #2978)
    • touched: apps/server/src/modules/rules/helpers/rule.comparator.service.ts, apps/server/src/modules/rules/tests/rule.comparator.service.executeRulesWithData.spec.ts, apps/ui/styles/globals.css
  • a5f40d6 fix(rules): robust community/YAML rule import across media servers (fix(rules): robust community/YAML rule import across media servers #2976)
    • touched: apps/server/src/modules/rules/constants/constants.service.ts, apps/server/src/modules/rules/helpers/section-operators.ts, apps/server/src/modules/rules/helpers/yaml.service.spec.ts, apps/server/src/modules/rules/helpers/yaml.service.ts, apps/server/src/modules/rules/rules.service.ts, apps/server/src/modules/rules/rules.service.updateRules.spec.ts, apps/server/src/modules/settings/rule-migration.service.spec.ts, apps/server/src/modules/settings/rule-migration.service.ts, apps/ui/src/components/Rules/RuleGroup/AddModal/index.tsx
  • 518b999 fix: OR rule sections incorrectly evaluated as AND due to operator coercion (fix: OR rule sections incorrectly evaluated as AND due to operator coercion #2971)
    • touched: apps/server/src/modules/rules/helpers/rule.comparator.service.ts, apps/server/src/modules/rules/helpers/yaml.service.spec.ts, apps/server/src/modules/rules/helpers/yaml.service.ts, apps/server/src/modules/rules/rules.service.ts, apps/server/src/modules/rules/rules.service.updateRules.spec.ts, apps/server/src/modules/rules/tests/rule.comparator.service.executeRulesWithData.spec.ts, apps/ui/src/components/Rules/Rule/RuleCreator/RuleInput/index.spec.tsx, apps/ui/src/components/Rules/Rule/RuleCreator/RuleInput/index.tsx

fix: commits that touched a doc-worthy surface — the UI, any server module except internal-only events/logging, any controller, or the README. Worth scanning to decide whether observable behavior changed enough to warrant a docs note.

Documentation-labeled issues & PRs

Merged PRs labeled documentation (1):

  • #2991 — fix(rules): scope exclusions to their rule group under TypeORM 1.0

Manually tagged with the documentation label — confirm each is reflected in Maintainerr_docs before release.

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Release 2.5 - Execute Push PR To Main completed after approval.

  • PR squash-merged into main
  • Sync back: success
  • Build Main: success

@maintainerr-automation

Copy link
Copy Markdown
Contributor Author

🎉 This PR is included in version 3.13.0 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:docker-build Build release candidate Docker image released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants