fix(books): layered fallback for comic Fetch Metadata SERIES/issue prefill (bookshelf-qnmg) #847
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-qnmg"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Fixes the comic Fetch Metadata form's SERIES/Issue fields being blank when
comic_metadata.volume_name/issue_numberare empty — even when the book had a Series association (e.g. "Doc Savage #2") or a parseable filename like "15761 Doc Savage 002".Root cause:
handler.gocomputed the prefill ONLY fromcd.VolumeName/cd.IssueNumber, with no fallback when those fields were empty.Fix: Layered fallback for
isComicbooks:cd.VolumeName→book.Series.Name→ parsed from primary CBX filename (strips leading numeric scan-id + trailing zero-padded issue number)cd.IssueNumber→book.Series.Number→ parsed from filenamecd.CoverDate[:4]only; left blank when absentNew helpers (all 100% covered, black-box tests):
comicSearchPrefill— orchestrates the layered fallbackparseComicFilenameParts— strips leading scan-id prefix + trailing zero-padded issue (e.g. "15761 Doc Savage 002" → series="Doc Savage", issue="2")primaryComicFileStem— extension-stripped name of first CBX file in book.FilesformatSeriesNumber— formats float64 as integer or minimal decimal stringTest plan
comic_search_prefill_test.go: cd with volume_name set; cd empty + book.Series set; cd nil + book.Series set; cd nil + no Series + filename-onlymake coveragepasses (zero uncovered blocks)go vet+golangci-lint run ./internal/books/...cleanCloses bead bookshelf-qnmg on merge.
Recompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)
Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.
CODE REVIEW — bookshelf-qnmg
Phase 0: DEMO Verification
No explicit
DEMOblock exists in the PR body or bead comments. This is a form-prefill UI fix whose observable behavior requires a live browser session, not a CLI-runnable command. CI E2E Browser tests are green (87c73a0b— all 6 check contexts: success). Treating as PARTIAL with valid reason; proceeding.Phase 1: Spec Compliance
All four scenarios from the bead description are covered: (a)
cdwithvolume_name; (b)cdpresent,volume_nameempty,book.Seriesset; (c)cd == nil,book.Seriesset; (d)cd == nil, no Series, filename-only. Edge cases (empty stem, all-zero issue, fractional series number, no CBX files) are present. Handler change is localized to the Show-handler prefill block — List handler and save path are untouched. ✓Phase 2: Code Quality
[MAJOR]
internal/books/export_test.go:101-107— three new var-exports widen the white-box shim when the public wrapper already existsThe file adds:
All three private functions are reachable through the
ComicSearchPrefillwrapper that is also added to this file. Every test case in theparseComicFilenamePartsandprimaryComicFileStemdescribes can be driven viaComicSearchPrefill(nil, books.Book{Files: []books.BookFile{{BookType: "cbx", FileName: "<stem>.cbz"}}}).formatSeriesNumber(2.1)maps toComicSearchPrefill(nil, books.Book{Series: &books.Series{Number: 2.1}}). Perproject-conventions.md: "Cover an unexported helper by driving the public caller that reaches it." TheComicSearchPrefillwrapper is exactly that public caller — adding three additional raw-var exports alongside it contradicts the rule the wrapper was added to satisfy. Fix: remove the three var-exports and consolidate their edge-case test contexts into the existingcomicSearchPrefill — layered fallbackDescribe block, driven throughComicSearchPrefill.[MINOR]
internal/books/comic_search_prefill_test.go:43— context description contradicts the fixtureThe context is titled
"with a long title and no leading scan-id"but the fixture is"15759 Tales of the Legion of Super-Heroes 354", which does have a leading scan-id (15759). The regex correctly strips it and the assertions are right; only the description is wrong. Rename to"with a scan-id prefix and a long multi-word title".[MINOR]
internal/books/comic_search_prefill.go:30-34— thecd == nilguard is dead code in the production call pathhandler.go:1244-1246guaranteescdis always non-nil forisComicbooks:This runs before
comicSearchPrefillis ever called, socomicSearchPrefillwill never receive a nilcdfromhandler.go. The fix is still correct (the empty&comicDisplay{}has emptyVolumeName/IssueNumber/CoverDatewhich triggers the fallback chain), but test scenarios (c) and (d) exercise a production-unreachable code path. Harmless, but worth noting so the next reader doesn't add a second nil-guard thinking it is needed.[MINOR]
internal/books/comic_search_prefill.go:43-46—Series.Number != 0silently skips issue #0Issue #0 exists in comics (preview/zero issues). Because
Series.Numberisfloat64, zero is the Go zero-value with no "not set" distinction. A book at issue #0 falls through to filename parsing instead. This is a pre-existing schema limitation (Grimmory float64 encoding), not introduced here, so it is not a blocker — but a comment explaining the zero-value skip would prevent future confusion.REVIEW VERDICT: 0 blocker, 1 major, 3 minor
Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.
Recompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)
7e36e36b790a8020c414Workflow Detail page screenshot (wf-detail-older-execution)
Older completed ContinueAsNew epoch detail — execution ID and state visible, Cancel absent.
Recompute Match Score — kebab open screenshot (recompute-match-score-kebab-open)