Skip to content

Improve seeding rule customization#553

Merged
Flaminel merged 34 commits into
mainfrom
improve_seeding_rules
Apr 11, 2026
Merged

Improve seeding rule customization#553
Flaminel merged 34 commits into
mainfrom
improve_seeding_rules

Conversation

@Flaminel

@Flaminel Flaminel commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Relates to #200

@Flaminel Flaminel requested a review from Copilot April 8, 2026 19:29
@Flaminel

Flaminel commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

@sourcery-ai review
@greptileai review

@greptile-apps

greptile-apps Bot commented Apr 8, 2026

Copy link
Copy Markdown

Greptile Summary

This PR significantly extends seeding rule customization by adding per-rule Categories (replacing the old name-as-category behavior), TrackerPatterns, TagsAny/TagsAll (for qBittorrent and Transmission), and Priority fields, along with a new SeedingRuleEvaluator service and a reorder endpoint. A data migration backfills existing rules from the old name field.

  • P1 — Categories not sanitized: CreateRule and UpdateSeedingRule call SanitizeStringList for every other string list (TrackerPatterns, TagsAny, TagsAll) but assign dto.Categories / ruleDto.Categories directly. Whitespace in a stored category silently prevents SeedingRuleEvaluator.Matches from ever matching that rule.

Confidence Score: 4/5

Safe to merge after fixing the Categories sanitization gap; all other logic is sound.

Two P1 findings (create and update paths both skip SanitizeStringList for Categories) mean rules with whitespace in category values will silently never fire. The fix is a one-liner in each location. Everything else — migration, evaluator logic, priority ordering, tag filtering, tests — looks correct.

code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs — both CreateRule and UpdateSeedingRule methods

Vulnerabilities

No security concerns identified. All endpoints are behind [Authorize], IDs are Guid-typed (no injection surface), and all DB access uses EF Core parameterized queries.

Important Files Changed

Filename Overview
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs New CRUD + reorder endpoints for seeding rules; Categories list skips sanitization on both create and update, causing potential silent match failures.
code/backend/Cleanuparr.Infrastructure/Services/SeedingRuleEvaluator.cs New evaluator that matches torrents to seeding rules by category, tracker pattern, tags (ITagFilterable), and privacy type; logic is correct and well-tested.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/SeedingRuleHelper.cs Helper for querying per-type seeding rule tables; clean dispatch pattern with both tracked and no-tracking variants.
code/backend/Cleanuparr.Persistence/Migrations/Data/20260408081126_AddSeedingRuleEnhancements.cs Adds categories, priority, tracker_patterns, tags_any, tags_all columns across all five seeding rule tables; data migration backfills categories from name and assigns sequential priorities.
code/backend/Cleanuparr.Infrastructure.Tests/Services/SeedingRuleEvaluatorTests.cs Comprehensive unit tests for the new evaluator using NSubstitute; covers category, tracker, tags, privacy, priority ordering, and edge cases.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/DownloadService.cs Base service wired to inject ISeedingRuleEvaluator; CleanDownloadsAsync delegates per-torrent rule selection to the evaluator correctly.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/ISeedingRule.cs Interface extended with Categories, TrackerPatterns, and Priority; clean design.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/ITagFilterable.cs New interface for tag-based filtering, implemented only by qBittorrent and Transmission rule models.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DelugeServiceFixture.cs Fixture updated to add ISeedingRuleEvaluator mock; uses Moq for the new mock while CLAUDE.md prefers NSubstitute for new additions.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Contracts/Requests/SeedingRuleRequest.cs Request record extended with Categories, TrackerPatterns, TagsAny, TagsAll, and optional Priority; validation annotations are appropriate.

Sequence Diagram

sequenceDiagram
    participant Client as API Client
    participant SC as SeedingRulesController
    participant SRH as SeedingRuleHelper
    participant DB as DataContext (SQLite)
    participant DS as DownloadService
    participant SRE as SeedingRuleEvaluator

    Client->>SC: POST /api/seeding-rules/{clientId}
    SC->>DB: GetForClientAsync (existing rules)
    SC->>SC: Auto-assign Priority (max+1) or use provided
    SC->>DB: AddRuleToDbSet + SaveChanges

    Client->>SC: PUT /api/seeding-rules/{clientId}/reorder
    SC->>SRH: GetForClientTrackedAsync
    SC->>SC: Validate IDs (no dupes, count match, all exist)
    SC->>DB: Assign sequential priorities + SaveChanges

    Note over DS,SRE: During cleanup job execution
    DS->>DS: FilterDownloadsToBeCleanedAsync (category pre-filter)
    DS->>SRH: GetForClientAsync (bulk load rules)
    loop Each torrent
        DS->>SRE: GetMatchingRule(torrent, rules)
        SRE->>SRE: OrderBy Priority → FirstOrDefault(Matches)
        SRE-->>DS: ISeedingRule? (matched rule)
        DS->>DS: ShouldCleanDownload(ratio, seedTime, rule)
        DS->>DS: DeleteDownload if threshold met
    end
Loading

Reviews (1): Last reviewed commit: "removed confusing doc phrases" | Re-trigger Greptile

@Cleanuparr Cleanuparr deleted a comment from sourcery-ai Bot Apr 8, 2026
@Cleanuparr Cleanuparr deleted a comment from SourceryAI Apr 8, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds tracker/tag-aware seeding rules with explicit priority ordering (including UI drag-and-drop reorder) to enable fine-grained cleanup policies per tracker and rule precedence (relates to #200).

Changes:

  • Extend seeding rules with categories, trackerPatterns, optional tag/label filters, and priority; evaluate rules by lowest priority first.
  • Add a reorder API endpoint plus frontend drag-and-drop UI to persist rule priority order.
  • Update persistence schema/migrations and expand automated coverage (backend unit tests + Playwright e2e).

Reviewed changes

Copilot reviewed 72 out of 73 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
e2e/tests/helpers/app-api.ts Adds API helpers for rule reordering and download client CRUD used by e2e tests.
e2e/tests/12-seeding-rules-api.spec.ts New Playwright coverage for CRUD + reorder semantics + validation.
docs/docs/configuration/download-cleaner/index.mdx Updates docs to describe rule-based matching (categories/tracker/tags) and priority evaluation.
code/frontend/src/app/shared/models/download-cleaner-config.model.ts Extends seeding rule model with new matching/prioritization fields and defaults.
code/frontend/src/app/features/settings/download-cleaner/download-cleaner.component.ts Adds modal fields for new rule criteria and drag-drop reorder behavior.
code/frontend/src/app/features/settings/download-cleaner/download-cleaner.component.scss Styles for draggable rule cards, priority badge, and hint text.
code/frontend/src/app/features/settings/download-cleaner/download-cleaner.component.html UI updates: categories badges, tracker-pattern count badge, reorder controls, and new form fields.
code/frontend/src/app/core/services/documentation.service.ts Adds doc anchors for new seeding rule fields.
code/frontend/src/app/core/api/download-cleaner.api.ts Adds reorderSeedingRules API call.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/UTorrentSeedingRule.cs Adds categories/tracker patterns/priority; validates categories non-empty.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/TransmissionSeedingRule.cs Adds categories/tracker patterns/tags + priority; implements tag filtering interface.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/RTorrentSeedingRule.cs Adds categories/tracker patterns/priority; validates categories non-empty.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/QBitSeedingRule.cs Adds categories/tracker patterns/tags + priority; implements tag filtering interface.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/ITagFilterable.cs New marker interface for rules supporting tag/label filtering.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/ISeedingRule.cs Expands seeding rule contract to include categories/tracker patterns/priority.
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/DelugeSeedingRule.cs Adds categories/tracker patterns/priority; validates categories non-empty.
code/backend/Cleanuparr.Persistence/Migrations/Data/DataContextModelSnapshot.cs Snapshot updates for new columns.
code/backend/Cleanuparr.Persistence/Migrations/Data/20260408081126_AddSeedingRuleEnhancements.Designer.cs New EF migration designer for schema enhancements.
code/backend/Cleanuparr.Persistence/Migrations/Data/20260408081126_AddSeedingRuleEnhancements.cs Migration adds new columns + data backfill + priority assignment.
code/backend/Cleanuparr.Persistence/DataContext.cs Adds JSON list conversions for new list fields on seeding rule entities.
code/backend/Cleanuparr.Persistence/Converters/JsonStringListConverter.cs New EF value converter to store List<string> as JSON in SQLite TEXT columns.
code/backend/Cleanuparr.Persistence.Tests/Models/Configuration/DownloadCleaner/SeedingRuleTests.cs Updates tests to populate required Categories.
code/backend/Cleanuparr.Infrastructure/Services/UriService.cs Refactors domain parsing regex to use GeneratedRegex.
code/backend/Cleanuparr.Infrastructure/Services/SeedingRuleEvaluator.cs New evaluator: priority-ordered matching across category/tracker/tags/privacy.
code/backend/Cleanuparr.Infrastructure/Services/QueueRuleManager.cs Renames queue rule manager implementation and logger types.
code/backend/Cleanuparr.Infrastructure/Services/QueueRuleEvaluator.cs Renames queue rule evaluator and updates injected interfaces.
code/backend/Cleanuparr.Infrastructure/Services/Interfaces/ISeedingRuleEvaluator.cs New interface for seeding rule matching logic.
code/backend/Cleanuparr.Infrastructure/Services/Interfaces/IQueueRuleManager.cs Renames queue rule manager interface.
code/backend/Cleanuparr.Infrastructure/Services/Interfaces/IQueueRuleEvaluator.cs Renames queue rule evaluator interface.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/UTorrent/UTorrentServiceQC.cs Switches to IQueueRuleEvaluator naming.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/UTorrent/UTorrentServiceDC.cs Updates filtering to use rule.Categories instead of rule.Name.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/UTorrent/UTorrentService.cs Updates constructor DI to use queue + seeding evaluators.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/UTorrent/UTorrentItemWrapper.cs Adds tracker domain extraction + tag list (empty for µTorrent).
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Transmission/TransmissionServiceQC.cs Switches to IQueueRuleEvaluator naming.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Transmission/TransmissionServiceDC.cs Updates filtering to use rule.Categories.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Transmission/TransmissionService.cs Requests Transmission labels field; updates constructor DI.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Transmission/TransmissionItemWrapper.cs Adds tracker domain extraction + exposes labels as tags.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/RTorrent/RTorrentServiceQC.cs Switches to IQueueRuleEvaluator naming.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/RTorrent/RTorrentServiceDC.cs Updates filtering to use rule.Categories.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/RTorrent/RTorrentService.cs Updates constructor DI to use queue + seeding evaluators.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/RTorrent/RTorrentItemWrapper.cs Adds tracker domain extraction + tag list (empty for rTorrent).
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/QBittorrent/QBitServiceQC.cs Switches to IQueueRuleEvaluator naming.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/QBittorrent/QBitServiceDC.cs Updates filtering to use rule.Categories.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/QBittorrent/QBitService.cs Updates constructor DI to use queue + seeding evaluators.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/QBittorrent/QBitItemWrapper.cs Adds tracker domain extraction + exposes qBittorrent tags.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/DownloadServiceFactory.cs Updates DI resolution to use new evaluator interfaces.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/DownloadService.cs Uses ISeedingRuleEvaluator for matching; updates rule selection logic.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Deluge/DelugeServiceQC.cs Switches to IQueueRuleEvaluator naming.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Deluge/DelugeServiceDC.cs Updates filtering to use rule.Categories.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Deluge/DelugeService.cs Updates constructor DI to use queue + seeding evaluators.
code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/Deluge/DelugeItemWrapper.cs Adds tracker domain extraction + tag list (empty for Deluge).
code/backend/Cleanuparr.Infrastructure.Tests/Services/SeedingRuleEvaluatorTests.cs New unit test suite for combined matching + priority ordering.
code/backend/Cleanuparr.Infrastructure.Tests/Services/QueueRuleManagerTests.cs Renames tests for queue rule manager.
code/backend/Cleanuparr.Infrastructure.Tests/Services/QueueRuleEvaluatorTests.cs Renames tests for queue rule evaluator.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/UTorrentServiceFixture.cs Updates fixtures for renamed interfaces + new seeding evaluator dependency.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/UTorrentServiceDCTests.cs Updates tests to provide Categories.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/TransmissionServiceTests.cs Updates expected requested fields to include labels.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/TransmissionServiceFixture.cs Updates fixtures for renamed interfaces + new seeding evaluator dependency.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/TransmissionServiceDCTests.cs Updates tests to provide Categories.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/RTorrentServiceFixture.cs Updates fixtures for renamed interfaces + new seeding evaluator dependency.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/RTorrentServiceDCTests.cs Updates tests to provide Categories.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/QBitServiceFixture.cs Updates fixtures for renamed interfaces + new seeding evaluator dependency.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/QBitServiceDCTests.cs Updates tests to provide Categories.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DownloadServiceFactoryTests.cs Updates service registrations for renamed/new interfaces.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DelugeServiceFixture.cs Updates fixtures for renamed interfaces + new seeding evaluator dependency.
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DelugeServiceDCTests.cs Updates tests to provide Categories.
code/backend/Cleanuparr.Domain/Entities/ITorrentItemWrapper.cs Adds tracker domains + tags to torrent wrapper contract.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/SeedingRuleHelper.cs Adds ordering and tracked-query helper for reorder persistence.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs Adds priority assignment, new fields on update, and reorder endpoint.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/DownloadCleanerConfigController.cs Extends config response to include new seeding rule fields.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Contracts/Requests/SeedingRuleRequest.cs Extends request contract with new matching/prioritization fields + validation.
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Contracts/Requests/ReorderSeedingRulesRequest.cs New request contract for reorder endpoint.
code/backend/Cleanuparr.Api/DependencyInjection/ServicesDI.cs Registers new evaluator + renamed queue rule services.
Files not reviewed (1)
  • code/backend/Cleanuparr.Persistence/Migrations/Data/20260408081126_AddSeedingRuleEnhancements.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread code/backend/Cleanuparr.Infrastructure/Services/UriService.cs
Comment thread docs/docs/configuration/download-cleaner/index.mdx
@Flaminel

Flaminel commented Apr 8, 2026

Copy link
Copy Markdown
Contributor Author

/review

@qodo-code-review

qodo-code-review Bot commented Apr 8, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0)   📘 Rule violations (2)   📎 Requirement gaps (0)   🎨 UX Issues (0)
📘\ ⚙ Maintainability (2)

Grey Divider


Action required

1. Moq Mock<> added in tests📘
Description
Modified test fixtures still create mocks via Moq (new Mock()) instead of NSubstitute, violating
the required backend unit test stack. This increases inconsistency across tests and blocks
standardization on NSubstitute/Shouldly.
Code

code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DelugeServiceFixture.cs[R40-42]

+        RuleEvaluator = new Mock<IQueueRuleEvaluator>();
+        RuleManager = new Mock<IQueueRuleManager>();
+        SeedingRuleEvaluator = new Mock<ISeedingRuleEvaluator>();
Evidence
The checklist requires backend unit tests to use xUnit + NSubstitute + Shouldly (and specifically
disallows introducing nonstandard mocking frameworks like Moq for new/modified test code). The PR
modifies test code and adds/keeps Moq mock creation via new Mock().

Rule 225592: Use xUnit with NSubstitute and Shouldly in backend unit tests
Rule 226562: Use NSubstitute for all new mocks in test code
code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DelugeServiceFixture.cs[40-42]
code/backend/Cleanuparr.Infrastructure.Tests/Services/QueueRuleEvaluatorTests.cs[46-52]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Modified test code uses Moq (`Mock<>`, `It.IsAny`, `Setup`) even though the standard test stack requires NSubstitute (and Shouldly assertions).
## Issue Context
This PR updates multiple test fixtures/evaluators and introduces/continues Moq-based mocks.
## Fix Focus Areas
- code/backend/Cleanuparr.Infrastructure.Tests/Features/DownloadClient/DelugeServiceFixture.cs[40-42]
- code/backend/Cleanuparr.Infrastructure.Tests/Services/QueueRuleEvaluatorTests.cs[46-52]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. ReorderSeedingRules missing XML docs 📘
Description
The new public API endpoint ReorderSeedingRules is added without XML documentation comments. This
violates the requirement to document public backend APIs for maintainability and API clarity.
Code

code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[R169-171]

+    [HttpPut("{downloadClientId}/reorder")]
+    public async Task<IActionResult> ReorderSeedingRules(Guid downloadClientId, [FromBody] ReorderSeedingRulesRequest request)
+    {
Evidence
The checklist requires XML documentation (at least ``, plus params/returns where applicable) for
public backend API symbols. The new controller action is public and lacks any /// XML doc block
above it.

Rule 225602: Require XML documentation comments for public backend APIs
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[169-171]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new public controller action `ReorderSeedingRules` has no XML documentation comments.
## Issue Context
Public backend API endpoints must include XML docs with at least `<summary>` and appropriate `<param>` / `<returns>` tags.
## Fix Focus Areas
- code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[169-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. ReorderSeedingRules missing Async suffix 📘
Description
The new controller action ReorderSeedingRules is declared async but its name does not end with
Async. This violates the coding convention requirement for async method naming consistency in
backend code.
Code

code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[170]

+    public async Task<IActionResult> ReorderSeedingRules(Guid downloadClientId, [FromBody] ReorderSeedingRulesRequest request)
Evidence
The coding conventions checklist flags async methods that do asynchronous work but do not use an
Async suffix. The new action is declared `public async Task<IActionResult>
ReorderSeedingRules(...)` without the required suffix.

Rule 225590: Enforce Microsoft C# Coding Conventions in Backend Code
code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[170-170]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
An async controller action is named without the `Async` suffix.
## Issue Context
Per backend naming conventions, async methods should end with `Async`.
## Fix Focus Areas
- code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[170-170]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (1)
4. Whitespace category matches uncategorized🐞
Description
SeedingRulesController persists Categories without trimming/removing whitespace-only entries, while
SeedingRuleEvaluator compares against (torrent.Category ?? string.Empty). A rule containing " " (or
similar) can therefore match torrents with null Category, causing unintended rule selection and
potentially unintended cleaning/deletion.
Code

code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[130]

+            existingRule.Categories = ruleDto.Categories;
Evidence
Categories are assigned directly from the request (no sanitization), and validation only enforces
list length—not that each element is non-empty after trimming. Torrent categories are explicitly
nullable, and the evaluator coalesces null to empty string; combined, a whitespace-only category
value can match uncategorized torrents.

code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[129-143]
code/backend/Cleanuparr.Domain/Entities/ITorrentItemWrapper.cs[27-33]
code/backend/Cleanuparr.Infrastructure/Services/SeedingRuleEvaluator.cs[17-23]
code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/DelugeSeedingRule.cs[51-62]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`Categories` is stored without trimming/removing whitespace-only entries. Because rule matching compares `torrent.Category ?? string.Empty`, whitespace/empty-like category values can accidentally match torrents with `Category == null`, leading to unintended seeding rule selection (and potentially unintended cleanup).
### Issue Context
- Request validation enforces `Categories.Count >= 1`, but does not ensure each category is non-empty after trimming.
- Model `Validate()` methods only check `Categories.Count`, not per-item content.
### Fix Focus Areas
- code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[129-143]
- code/backend/Cleanuparr.Api/Features/DownloadCleaner/Controllers/SeedingRulesController.cs[261-343]
- code/backend/Cleanuparr.Infrastructure/Services/SeedingRuleEvaluator.cs[17-23]
- code/backend/Cleanuparr.Persistence/Models/Configuration/DownloadCleaner/*SeedingRule.cs[51-72]
### Suggested fix
1. Apply the existing `SanitizeStringList(...)` to `Categories` on both create and update (and consider `Distinct(StringComparer.OrdinalIgnoreCase)` to avoid duplicates).
2. After sanitization, reject empty results (`BadRequest`) or let `Validate()` enforce it.
3. Strengthen `Validate()` in seeding rule models to ensure no category is null/whitespace after trimming.
4. (Optional hardening) In the evaluator, avoid treating `null` category as `string.Empty` if you want “uncategorized” to never match unless explicitly supported.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Event category now wrong🐞
Description
DownloadService publishes seedingRule.Name as the download-cleaned event/notification
“categoryName”, but Name is now a human-readable rule label separate from the actual torrent
categories. This makes notifications/events report a rule label in the “Category” field, breaking
semantics for users and any consumers expecting the actual category.
Code

code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/DownloadService.cs[129]

+            await _eventPublisher.PublishDownloadCleaned(torrent.Ratio, seedingTime, seedingRule.Name, result.Reason);
Evidence
The frontend now treats name as “Rule Name” (a descriptive label) and introduces a separate
categories field. Despite this, DownloadService still passes seedingRule.Name into
PublishDownloadCleaned(..., categoryName, ...), and NotificationPublisher displays that value
under the “Category” key.

code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/DownloadService.cs[120-130]
code/frontend/src/app/features/settings/download-cleaner/download-cleaner.component.html[227-241]
code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs[191-211]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`PublishDownloadCleaned` is still fed a value named `categoryName`, but the caller now passes `seedingRule.Name` (rule label). Notifications/events therefore show the rule label in the “Category” field.
### Issue Context
- UI explicitly separates `Rule Name` from `Categories`.
- Notifications render `categoryName` under the `Category` key.
### Fix Focus Areas
- code/backend/Cleanuparr.Infrastructure/Features/DownloadClient/DownloadService.cs[120-130]
- code/backend/Cleanuparr.Infrastructure/Events/Interfaces/IEventPublisher.cs[1-30]
- code/backend/Cleanuparr.Infrastructure/Events/EventPublisher.cs[186-204]
- code/backend/Cleanuparr.Infrastructure/Features/Notifications/NotificationPublisher.cs[191-211]
### Suggested fix
Choose one of:
1. **Preserve contract:** pass the actual torrent category (e.g., `torrent.Category ?? ""`) as `categoryName` and, if desired, add a new field (e.g., `ruleName`) in the event payload/notification context.
2. **Rename contract (breaking):** rename `categoryName` to `ruleName` across `IEventPublisher`, `EventPublisher`, and `NotificationPublisher`, and update notification labels from `Category` to `Rule`.
Option (1) is safer for existing consumers: keep `Category` meaning “torrent category” and add `Rule Name` separately.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

@Flaminel Flaminel force-pushed the improve_seeding_rules branch from 390911b to 9b9c395 Compare April 11, 2026 12:56
@Flaminel Flaminel merged commit 4e9d20d into main Apr 11, 2026
9 of 12 checks passed
@Flaminel Flaminel deleted the improve_seeding_rules branch April 11, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants