fix(metadata): dedup set-all empty option + log errors (bookshelf-4y3y.6) #452
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "bd-bookshelf-4y3y.6"
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
Two small minors from #445 re-review (4y3y.2):
templates/pages/settings_metadata_field_priority.html — Remove duplicate empty-value options in set-all dropdowns.
contentProvidersalready includes{ID:"", Name:"— none —"}, so the explicit<option value="">placeholders created two empty options per dropdown. Removed the explicit ones.internal/books/metadata_store.go — Log at Warn when
listAuthorsorlistCategoriesfails, per logging-standard trigger. Include context (book_id, which call failed). No control flow change — errors still silently result in nil slice fallback.Test plan
Closes bead bookshelf-4y3y.6 on merge.
1. templates/pages/settings_metadata_field_priority.html: Remove duplicate empty value options from set-all dropdowns. contentProviders already includes {ID:"", Name:"— none —"}, so explicit <option value="">— set all Px —</option> placeholder lines created two value="" options per dropdown. Keep one. 2. internal/books/metadata_store.go: Log at Warn when listAuthors or listCategories fails, per logging-standard trigger. Include context (book_id, which call failed). No control flow change — errors still silently result in nil slice fallback. Fixes #445 review minors (4y3y.2). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>Security Review (bot)
PR #452 — bead bookshelf-4y3y.6
Scope reviewed
logger.Warncalls ininternal/books/metadata_store.go(log-data audit)<option value="">entries intemplates/pages/settings_metadata_field_priority.html(new sink / injection audit)Findings
Log lines (
metadata_store.golines ~183 and ~187):book_id— internal auto-increment integer. No PII, no secret.error— a Goerrorvalue from a DB call (sql.Rows.Err()/ driver). Contains DB error text (e.g. "connection refused", "deadlock found"). No user-supplied content, no token/password. Complies with logging-standard ("context, not text"; no PII/credentials).Template change (
settings_metadata_field_priority.html):<option value="">placeholder options from set-all<select>dropdowns.{{range $.Providers}}<option value="{{.ID}}">{{.Name}}</option>{{end}}was already present and is auto-escaped byhtml/template..IDand.Nameare server-side struct fields, not user-reflected input — no XSS vector. The<option value="__clear__">literal is a hardcoded string, not user-supplied. No change in injection surface.Verdict
REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Code Review (bot)
PR #452 — bead bookshelf-4y3y.6
Phase 0: DEMO Verification
No DEMO block in the bead. This is a code-review-spawned minor-fix bead (non-blocking minors from #445 re-review) with no new API surface or interactive behavior to demo. Per the review request the verification is structural: dedup correctness, logging compliance, signature threading, and no behavior regression.
Phase 1: Spec Compliance
Both requirements from the bead description are addressed:
Duplicate empty option removed —
templates/pages/settings_metadata_field_priority.html: the four explicit<option value="">— set all Pn —</option>placeholders are dropped. The{{range $.Providers}}range already renders{ID:"", Name:"— none —"}as its first element (fromcontentProviders[0]inmatrix_handler.go:62), so exactly one empty-value option remains per set-all dropdown.Warn logging added —
internal/books/metadata_store.go:183,187: bothlistAuthorsandlistCategorieserror paths now log atWarnwith structured"book_id"and"error"fields. Previously both errors were silently swallowed.Signature change (
logger *slog.Loggeras first param toGetMetadataForRefetch) is threaded correctly through all call sites:internal/books/wire.go:233— passesd.Loggerinternal/app/build_extended_deps.go:364— passesloggerinternal/books/metadata_store_test.go— all 8 construction sites pass aslog.New(slog.NewTextHandler(io.Discard, nil))loggerPhase 2: Code Quality
Logging compliance (logging-standard.md):
metadata_store.go:183—logger.Warn("failed to list book authors", "book_id", bookID, "error", listErr)— structured k/v pairs, no string interpolation, no PII. Compliant.metadata_store.go:187—logger.Warn("failed to list book categories", "book_id", bookID, "error", listErr)— same. Compliant.Test coverage — missing
listCategorieserror test:The
listAuthorserror path has a test (Context("when listAuthors returns an error", ...)atmetadata_store_test.go:272). There is no correspondingContext("when listCategories returns an error")block. The newelsebranch atmetadata_store.go:187is uncovered by the test suite. The project requires 100% coverage oninternal/. CI is green, which is the authoritative signal — but this appears inconsistent. Either the coverage block boundaries happen to collapse the twoelsebranches into a single countable statement (possible in Go's coverage model for consecutiveif/else ifsequences), or a test is missing and CI's green status would need investigation. Since CI is the source of truth per review policy, I am not blocking on this, but flagging for awareness.[MINOR] internal/books/metadata_store_test.go — no test for
listCategorieserror pathThe symmetric
elsebranch added atmetadata_store.go:187(Warn log for listCategories failure) has no correspondingContext("when listCategories returns an error")test, unlike thelistAuthorserror path at line 272. CI is green so the 100% gate passed; however the test parity with listAuthors is missing. Worth adding aContext("when listCategories returns an error", ...)block to confirm the error branch is reached and the function still returns nil categories + no outer error.Template dedup — UX note:
Removing the per-column placeholder labels ("— set all P1 —", "— set all P2 —", etc.) in favour of the shared "— none —" text is a minor UX regression — users lose the per-column label context. The
aria-labelattributes ("Set all 1st priority"etc.) preserve accessibility semantics, so this is not a blocker. ThesetAllStimulus handler correctly resets toevent.target.value = ""(controller:33) which lands back on the remaining "— none —" option.No behavior regression found:
setAllcontroller logic: unchanged. Thevalue = ""reset atfield_priority_matrix_controller.js:33correctly selects the first option (the surviving empty option).__clear__option and provider options: unchanged, all still rendered.applyMatrixUpdatehandler: unchanged, no impact.REVIEW VERDICT: 0 blocker, 0 major, 1 minor