Skip to content

fix(search): stop reporting a series name as the series identifier - #986

Open
m4bard wants to merge 1 commit into
Listenarrs:canaryfrom
m4bard:fix/search-fallback-series-asin
Open

m4bard wants to merge 1 commit into
Listenarrs:canaryfrom
m4bard:fix/search-fallback-series-asin

Conversation

@m4bard

@m4bard m4bard commented Sep 14, 2026

Copy link
Copy Markdown

Fixes #979.

SearchResponseMapper.MapMetadataResultToAudibleAsync falls back to synthesizing an Audible-shaped result when the Audible product lookup returns nothing. In that fallback, the synthesized series entry puts the series name into its asin field, rather than leaving it null. The two non-fallback shapes elsewhere in the same file map s.Asin honestly, so a caller cannot tell from the response which branch produced the result it got. This matters now because three separate consumers have started reading a series ASIN off search results into the database: AudiobookSeriesMembership.SeriesAsin, the library-import wizard's membership construction, and AudibleBookMetadata.seriesAsin as a legacy fallback identifier. A series name landing in the one field meant to be a stable identifier is worse than merely cosmetic.

The fix is one line: emit asin = (string?)null in the fallback series entry instead of the series name, matching what the author fallback three lines above already does for the identical reason.

Four tests in SearchResponseMapperSeriesFallbackTests, driven through a metadata service that returns no Audible product so the fallback path is what's under test: the series entry reports no identifier, it still carries name and position, the author fallback beside it is asserted to share the same convention, and a result with no series emits an empty list. The first fails against current code with Expected: Null, Actual: String.

Suite run against current canary (upstream/canary at a630572e9, which this branch is built on): targeted filter SearchResponseMapperSeriesFallbackTests, 4 passed, 0 failed.

Once this lands, the client-side shape check the library-import wizard currently uses as a guard (which #847 already tightened, but which is leaky by construction) can be removed rather than maintained. Scope is one line plus the test file; out of #847's scope, per that PR's own body.

Disclosure: drafted with Claude Code at my direction; I read the cited code at commit a630572 and reviewed this before posting.

MapMetadataResultToAudibleAsync falls back to the fields a
MetadataSearchResult already carries when the Audible product lookup
cannot be made. That result holds the series as a name only, with no
identifier, and the fallback filled the series entry's asin with a copy
of the name.

Every client downstream treats that field as an identifier. The library
import wizard reads it, keeps it because it is the right length, and
posts it as a membership's seriesAsin, so a series name lands in the one
column whose value is that it is not a name. Foundation, Bartimaeus and
Peripheral are all ten characters and all real series.

The author fallback three lines above already reports a null asin for
exactly this reason. The series fallback now matches it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@m4bard
m4bard requested a review from a team September 14, 2026 09:20
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.

The advanced-search fallback returns the series name in the series asin field

1 participant