Skip to content

fix(slskd): year-date early rejects, rejected-source grabs, retry poisoning - #76

Merged
chodeus merged 6 commits into
mainfrom
fix/year-date-rejects-and-retry-poisoning
Aug 7, 2026
Merged

fix(slskd): year-date early rejects, rejected-source grabs, retry poisoning#76
chodeus merged 6 commits into
mainfrom
fix/year-date-rejects-and-retry-poisoning

Conversation

@chodeus

@chodeus chodeus commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Context

Live audit of a stuck single (GLXY – Proposition / Mind Less, two grabs on 2026-08-06, zero imports) traced four interacting defects across search, grab, and retry. Full chain, byte-verified against slskd transfer records and Lidarr history:

  1. Folder-year publish dates starve searches. A "(2017)" folder became PublishDate = 2017-01-01; with an Early Download Limit set on the indexer, Lidarr permanently rejected every report "published" before the album's actual mid-2017 release date. All ten coherent FLAC sources died this way (~400 rejections/day across the library); only year-less folders survived.
  2. Parser rejections were advisory. The surviving FLAC was a partial source the parser had already rejected (covers 1/2 matchable tracks — rejecting partial source), but MatchedSearchCriteria=false only affected tier-stop counting — the release still reached the decision engine and won. The download held two versions of the same track and could never complete the 2-track single.
  3. Tagged files became unclaimable. The pre-import tagger's tag write grew the file and feat-strip renamed it (4. GLXY - Mind Less.flac02 - Mind Less.flac, +31 KB), so the basename+size ownership guard retained it on every cleanup. With batch destinations pinned per album, the leftover contaminated every retry: Album match 76.7% vs 80%, has unmatched tracks, forever.
  4. Restart ghosts. slskd keeps succeeded transfers ~24 h, so a restart re-attached already-failed/imported downloads; replayed DownloadDirectoryComplete events post-processed long-gone folders and the vanished-files check re-failed an already-failed download — resolving its folder to another album's name when the single had been plucked from an album share.

Changes

  • AlbumData: only day-precision dates carry publish semantics; year/month synthesis and missing dates report discovery time. Pre-release protection still works for real future dates. The (YYYY) title suffix is unchanged.
  • SlskdItemsParser: folder years are tagged year-precision.
  • SlskdIndexerParser: automatic searches drop unmatched albums at parse time (new dropped … unmatched counter in the parse summary); interactive searches still emit everything.
  • PreImportTagger: TaggingResult now reports each successful tag's original and final path.
  • SlskdDownloadItem: post-tag identities overlay the enqueued basename+size map (BuildOwnedFileSizes), so the ownership guard can claim tagged/renamed files; new IsTerminalDownloadEvent predicate.
  • SlskdDownloadManager: records tagged identities after the tag pass; post-processing and the vanished-files failure skip downloads whose history is terminal (re-attachment itself stays, for queue visibility).

Verification

  • 386 tests pass (17 new: publish-date precision rules, the live partial-source classification case, the ownership overlay rename/rewrite cases, terminal-event truth table).
  • The live incident's arithmetic is reproduced in the tests (39,281,821 → 39,312,677 bytes rename case).
  • Config-side mitigation (clearing the indexer Early Download Limit) was applied to the live instance and immediately grabbed a previously-rejected coherent "(2017)" source; this PR removes the need for that workaround.

Summary by CodeRabbit

  • Bug Fixes

    • Corrected publish dates so year-only or missing dates no longer appear as misleading exact dates.
    • Improved download tracking after files are renamed or modified during tagging.
    • Prevented incomplete or already-finished downloads from being incorrectly flagged as failures.
    • Improved cleanup recognition for tagged files and renamed tracks.
  • Search

    • Automatic searches now exclude releases that do not match the requested criteria, while interactive searches continue to show them.
  • Metadata

    • Folder-derived years are now preserved as year-only information instead of being treated as full publish dates.

chodeus added 4 commits August 7, 2026 07:08
A folder year parsed from the share name became a Jan-1 PublishDate, and
Lidarr's EarlyReleaseSpecification permanently rejects any report published
more than the early-limit before the album's release date — so every
well-named "(2017)" source lost to a mid-2017 album, leaving only year-less
(often partial) folders grabbable. Live 2026-08-06: all ten coherent FLAC
sources for a single were rejected this way, ~400 rejections/day overall.

- AlbumData: only a day-precision date carries publish semantics; year/month
  synthesis and missing dates report discovery time (age 0). Pre-release
  protection still works for real dates: a future day-precision date stays
  early until release.
- SlskdItemsParser: tag folder years as year precision. The "(YYYY)" title
  suffix is unchanged.
MatchedSearchCriteria=false only steered tier-stop counting — the release
still reached Lidarr's decision engine and could win the grab once quality
or date specs killed the matched sources. Live 2026-08-06: a partial single
source the parser had logged as "rejecting partial source" was grabbed
twice in one day and could never import (it held two versions of the same
track).

Automatic searches now drop unmatched albums at parse time (counted in the
parse summary as dropped); interactive searches keep emitting everything so
the operator can still pick.
Two lifecycle gaps from the 2026-08-06 live audit:

- The pre-import tagger's tag write grows a file (padding/artwork) and
  feat-strip renames it, so the basename+size ownership guard could no
  longer claim it — the leftover was retained on every delete and, with
  batch destinations pinned per album, poisoned every retry of the same
  album (import failed at "has unmatched tracks" forever). The tagger now
  reports each successful tag's original and final path, and the manager
  re-learns the file's identity on the item, so the guard can claim it.

- Restart rehydrates items for downloads slskd still lists (succeeded
  transfers persist ~24h) even when Lidarr already resolved them. Replayed
  DownloadDirectoryComplete events then post-processed folders that were
  legitimately gone ("Folder missing after DownloadDirectoryComplete"),
  and the vanished-files check re-failed an already-failed download after
  every restart — with the folder guess naming another album's folder when
  the single was plucked from an album share. Post-processing and the
  vanished-files failure now skip downloads whose history is terminal;
  re-attachment itself stays (queue visibility).
Publish-date precision rules (year/none/day), the coherent-source
classifier on the live partial-source case, the tagged-file ownership
overlay (rename + in-place rewrite), and the terminal-event predicate.
386 tests total.
@chodeus chodeus added the release:patch Merge to main → patch release label Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1e4c538e-a00a-45e6-b9a8-dd19a31c202a

📥 Commits

Reviewing files that changed from the base of the PR and between daafc3b and 275c741.

📒 Files selected for processing (5)
  • src/Sleezer/Core/Model/AlbumData.cs
  • src/Sleezer/Core/PostProcessing/PreImportTagger.cs
  • src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs
  • src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs
  • src/Sleezer/Indexers/Soulseek/SlskdIndexerParser.cs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/Sleezer/Core/Model/AlbumData.cs
  • src/Sleezer/Indexers/Soulseek/SlskdIndexerParser.cs
  • src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs
  • src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs
  • src/Sleezer/Core/PostProcessing/PreImportTagger.cs

📝 Walkthrough

Walkthrough

The changes refine Soulseek release-date handling and automatic search filtering. They also track tagged file identities for ownership cleanup and prevent repeat processing of terminal downloads. Tests cover metadata, matching, ownership overlays, and terminal events.

Changes

Soulseek processing updates

Layer / File(s) Summary
Release metadata and automatic search filtering
src/Sleezer/Core/Model/AlbumData.cs, src/Sleezer/Indexers/Soulseek/*, tests/Sleezer.Tests/SlskdPublishDateAndOwnershipTests.cs
Day-precision dates remain unchanged. Year-precision and missing dates use discovery time. Automatic searches discard unmatched releases, while interactive searches retain them.
Tagged file identity and ownership overlay
src/Sleezer/Core/PostProcessing/PreImportTagger.cs, src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs, src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs, tests/Sleezer.Tests/SlskdPublishDateAndOwnershipTests.cs
Tagging reports original and final paths. Download ownership includes renamed and size-modified tagged files.
Terminal download lifecycle handling
src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs, src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs, tests/Sleezer.Tests/SlskdPublishDateAndOwnershipTests.cs
Failed, ignored, and imported events are terminal. Terminal downloads are not re-failed or post-processed.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PreImportTagger
  participant SlskdDownloadManager
  participant SlskdDownloadItem
  PreImportTagger-->>SlskdDownloadManager: Return TaggingResult with final file paths
  SlskdDownloadManager->>SlskdDownloadItem: RecordTaggedFile(basename, size)
  SlskdDownloadManager->>SlskdDownloadItem: BuildOwnedFileSizes()
Loading

Possibly related PRs

  • chodeus/sleezer#72: Both changes cover tagging renames and ownership-safe Soulseek cleanup.
  • chodeus/sleezer#73: Both changes update Soulseek file identity tracking and download recovery behavior.
  • chodeus/sleezer#60: This change extends the title-based fallback tagging flow introduced there.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.76% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main slskd fixes for date handling, rejected sources, and retry behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/year-date-rejects-and-retry-poisoning

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/Sleezer/Core/PostProcessing/PreImportTagger.cs (1)

130-134: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reduce narrative comments to concise gotchas.

Keep each comment to one or two lines. State the required invariant. Remove live-incident history and extended consequence detail.

  • src/Sleezer/Core/PostProcessing/PreImportTagger.cs#L130-L134: Keep only that successful tagging must report original and final paths for ownership tracking.
  • src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs#L85-L90: Keep only that tagged files can change basename and size.
  • src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs#L1104-L1108: Keep only that terminal rehydrated downloads must not start post-processing.
  • src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs#L1205-L1208: Keep only that ownership identities must be refreshed after tagging.

As per path instructions, comments must be navigational or gotcha-only and limited to one or two lines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Sleezer/Core/PostProcessing/PreImportTagger.cs` around lines 130 - 134,
Shorten the comments at
src/Sleezer/Core/PostProcessing/PreImportTagger.cs#L130-L134,
src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs#L85-L90,
src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs#L1104-L1108, and
src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs#L1205-L1208 to one
or two lines each. Retain only the required gotchas: successful tagging reports
original and final paths for ownership tracking; tagged files may change
basename and size; terminal rehydrated downloads must not start post-processing;
and ownership identities must be refreshed after tagging. Remove incident
history and extended consequences.

Source: Path instructions

src/Sleezer/Core/Model/AlbumData.cs (1)

84-89: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep added comments to short operational gotchas.

Both comments include historical context and incident narrative. Keep only the invariant needed at each code path.

  • src/Sleezer/Core/Model/AlbumData.cs#L84-L89: state that only day-precision dates populate PublishDate; year/month dates use discovery time.
  • src/Sleezer/Indexers/Soulseek/SlskdIndexerParser.cs#L162-L168: state that unmatched releases are retained for interactive searches and dropped for automatic searches.

As per path instructions, comments must be navigational/gotcha notes limited to 1–2 lines.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Sleezer/Core/Model/AlbumData.cs` around lines 84 - 89, Shorten the
comment at src/Sleezer/Core/Model/AlbumData.cs#L84-L89 to state only that
day-precision dates populate PublishDate, while year/month-precision dates use
discovery time. Also shorten the comment at
src/Sleezer/Indexers/Soulseek/SlskdIndexerParser.cs#L162-L168 to state that
unmatched releases are retained for interactive searches and dropped for
automatic searches; keep each comment to 1–2 lines.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs`:
- Around line 947-953: The vanished-file handling in the surrounding download
reconciliation logic must use IsTerminalDownloadEvent instead of
IsPoisonedHistoryEvent, so DownloadImportIncomplete remains non-terminal and
completed items with missing folders are failed and retried. Add a regression
test covering DownloadImportIncomplete with a missing completed folder,
asserting it is treated as non-terminal.

---

Nitpick comments:
In `@src/Sleezer/Core/Model/AlbumData.cs`:
- Around line 84-89: Shorten the comment at
src/Sleezer/Core/Model/AlbumData.cs#L84-L89 to state only that day-precision
dates populate PublishDate, while year/month-precision dates use discovery time.
Also shorten the comment at
src/Sleezer/Indexers/Soulseek/SlskdIndexerParser.cs#L162-L168 to state that
unmatched releases are retained for interactive searches and dropped for
automatic searches; keep each comment to 1–2 lines.

In `@src/Sleezer/Core/PostProcessing/PreImportTagger.cs`:
- Around line 130-134: Shorten the comments at
src/Sleezer/Core/PostProcessing/PreImportTagger.cs#L130-L134,
src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs#L85-L90,
src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs#L1104-L1108, and
src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs#L1205-L1208 to one
or two lines each. Retain only the required gotchas: successful tagging reports
original and final paths for ownership tracking; tagged files may change
basename and size; terminal rehydrated downloads must not start post-processing;
and ownership identities must be refreshed after tagging. Remove incident
history and extended consequences.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6fff9f22-4814-4ab0-acfb-7896600cf22c

📥 Commits

Reviewing files that changed from the base of the PR and between 3cd8a90 and daafc3b.

📒 Files selected for processing (7)
  • src/Sleezer/Core/Model/AlbumData.cs
  • src/Sleezer/Core/PostProcessing/PreImportTagger.cs
  • src/Sleezer/Download/Clients/Soulseek/Models/SlskdDownloadItem.cs
  • src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs
  • src/Sleezer/Indexers/Soulseek/SlskdIndexerParser.cs
  • src/Sleezer/Indexers/Soulseek/SlskdItemsParser.cs
  • tests/Sleezer.Tests/SlskdPublishDateAndOwnershipTests.cs

Comment thread src/Sleezer/Download/Clients/Soulseek/SlskdDownloadManager.cs Outdated
@chodeus
chodeus merged commit bf99a1c into main Aug 7, 2026
3 checks passed
@chodeus
chodeus deleted the fix/year-date-rejects-and-retry-poisoning branch August 7, 2026 00:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:patch Merge to main → patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant