Agent Brief
Category: bug Summary: A usable cross-seed result from one indexer suppresses the retry without a year for every other indexer, leaving valid matches undiscovered when metadata years differ. Why a human: Design: ADR 0007 deliberately keeps the yearless retry global to limit extra requests. Decide how to recover these missed matches and record any reversal of that decision before implementation.
Investigation results
Two searches for the same movie release were investigated on 19 October 2026. Indexers are anonymized as A, B, C, and D.
- Initial on-completion search, with A as the source: A was excluded because the source already used it. B, C, and D were eligible. C and D both received movie searches containing IMDb and TMDb IDs together with
year=2018. Neither contributed candidates to the returned result set. The search returned 11 candidates overall; 10 were rejected by release filtering and one strict, exact-size match from B was accepted and added. No retry without the year ran. - Later manual search, using the newly added copy from B as the source: A and B were excluded as already covered. The initial search with the year produced nothing usable. The search retried without the year and found strict, exact-size matches on both C and D. Both were then added successfully.
The source release name and reported size were the same across these searches. This was a comparison of recorded runs, not a controlled replay of identical requests with the cache bypassed.
The maintainer reports that IMDb and TMDb metadata can assign release years differing by one year, and individual indexers may use different providers. Adding the release-name year to an ID search can therefore hide the correct release. The recorded requests establish that both IDs were supplied to C and D; they do not establish which ID each endpoint prioritized or its stored metadata year.
Current behavior
searchGatherer.gather runs the yearless retry only when the entire primary result set contains no usable result. A usable result on B therefore prevents C and D from receiving that retry, even when they have no usable results of their own.
The later manual search had a different eligible indexer set because B was already present. Removing B from that set allowed the existing whole-search retry to run, exposing the additional matches. The observed difference comes from the shared retry gate and changed search state; it does not require different matching rules for completion and manual searches.
This is deliberate existing policy, not a newly established regression. ADR 0007 rejected a per-indexer yearless retry because it could add requests for nearly every movie and was considered to have low value per indexer. These results provide a concrete missed-match case to reassess against that cost.
Desired behavior
Movie cross-seed discovery should recover valid matches hidden by differing metadata years even when another indexer has already supplied a usable match. Apply the chosen policy in the shared search behavior so completion and manual callers receive the same treatment for equivalent inputs.
Key interfaces
searchGatherer.gather: owns retry selection, merged results, and indexer coverage.TorznabSearchRequest: carries the external IDs, year, and selected indexers.applyCapabilitySpecificParams: retains advertised ID parameters; it does not choose an IMDb/TMDb preference.TestGatherSearchResults: currently asserts that one usable result suppresses the yearless retry.
Acceptance criteria
- Settle and document the retry/request policy, including the additional request cost and any ADR update required.
- Cover a movie search where one indexer matches immediately and another exposes a valid match only when the conflicting year is absent.
- Verify equivalent completion and manual searches use the chosen shared policy.
- Preserve release and size validation, retain primary matches, and deduplicate merged results.
- Preserve cancellation, partial-result handling, rate limits, and accurate indexer coverage when a retry fails.
Out of scope
- Tracker-specific rules or a hardcoded IMDb/TMDb preference list.
- Relaxing release or size matching to compensate for missing search results.
- Changes to TV searches or unrelated retry passes.
Domain
- Glossary terms: Usable result, Arr IDs, Mixed mode, Per-indexer retry, Trigger. The glossary currently distinguishes the global yearless retry from per-indexer retries.
- ADRs: 0007, Torznab search passes are one gatherer with an injected usable predicate. It explicitly requires a new ADR if the yearless retry becomes per-indexer.
- Owner: the cross-seed search gatherer; indexer capability handling belongs to the Torznab service.
Validation and logs
- Current code inspected at
80baea327bf377b6bd54525f85eda98a2388f023. - Existing focused checks passed:
go test -count=1 ./internal/services/crossseed -run 'TestGatherSearchResults/year_set_(and_nothing_usable|with_one_usable_hit)'. - These tests confirm the current retry gate; they do not replay a live metadata-year mismatch.
- Local logs were reviewed and summarized above. No public log attachment, tracker names, tracker URLs, torrent names, hashes, or credentials are included.
One solution to bypass the broad retry, is to remove
yearfrom searches containing valid ids.ID's are an accepted source of truth, and perhaps shouldn't be polluted by an additional year constraint.