feat: remove obsolete dead-provider metadata UI (bookshelf-o0m9) #779

Merged
zombor merged 3 commits from bd-bookshelf-o0m9 into main 2026-06-25 19:03:43 +00:00
Owner

Summary

  • Removes Goodreads/Amazon/Audible weight cards from all 3 match-weights settings sections (global, per-library override, per-library defaults)
  • Removes Goodreads/Amazon/Audible provider rating display blocks from book detail page
  • Removes Goodreads ID field from book detail identifiers editor
  • Removes AvgGoodreads rating badge from series detail page
  • Shrinks MatchWeights struct 24→19 fields; DefaultMatchWeights total 87→79; MatchScoreInput drops 10 dead fields
  • DB columns (goodreads_rating, amazon_rating, audible_rating, etc.) intentionally preserved for Grimmory compat

Test plan

  • make test — all unit tests pass
  • npm run coverage — JS gate 100% maintained
  • No lint issues in this worktree (other-worktree lint issues pre-exist)
  • Match-score calculations updated to use correct /79 denominator
  • Templates verified: no dead field references remain

Closes bead bookshelf-o0m9 on merge.

## Summary - Removes Goodreads/Amazon/Audible weight cards from all 3 match-weights settings sections (global, per-library override, per-library defaults) - Removes Goodreads/Amazon/Audible provider rating display blocks from book detail page - Removes Goodreads ID field from book detail identifiers editor - Removes AvgGoodreads rating badge from series detail page - Shrinks `MatchWeights` struct 24→19 fields; `DefaultMatchWeights` total 87→79; `MatchScoreInput` drops 10 dead fields - DB columns (goodreads_rating, amazon_rating, audible_rating, etc.) intentionally preserved for Grimmory compat ## Test plan - [x] `make test` — all unit tests pass - [x] `npm run coverage` — JS gate 100% maintained - [x] No lint issues in this worktree (other-worktree lint issues pre-exist) - [x] Match-score calculations updated to use correct /79 denominator - [x] Templates verified: no dead field references remain Closes bead bookshelf-o0m9 on merge.
feat(o0m9): remove obsolete dead-provider metadata UI and weight fields
All checks were successful
/ JS Unit Tests (pull_request) Successful in 54s
/ Lint (pull_request) Successful in 2m33s
/ E2E API (pull_request) Successful in 2m3s
/ E2E Browser (pull_request) Successful in 3m25s
/ Integration (pull_request) Successful in 3m43s
/ Test (pull_request) Successful in 4m22s
c07bdff980
Strip Goodreads/Amazon/Audible from every UI surface: match-weights
settings cards (global + per-library override + defaults sections), book
detail provider-rating display blocks, Goodreads ID identifier row, and
series AvgGoodreads rating badge. Shrinks MatchWeights struct from 24→19
fields, MatchScoreInput drops 10 dead fields, DefaultMatchWeights total
87→79. DB columns (goodreads_rating, amazon_rating, audible_rating, etc.)
are intentionally preserved for Grimmory compatibility.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Author
Owner

04_series_detail.png

04_series_detail.png

**04_series_detail.png** ![04_series_detail.png](https://git.zombor.net/attachments/3be9a6ce-8c88-4bea-8642-e6b06fba9c41)
Author
Owner

01_book_detail.png

01_book_detail.png

**01_book_detail.png** ![01_book_detail.png](https://git.zombor.net/attachments/40793ca4-9d5e-4575-8ad4-1b756c5f99fa)
Author
Owner

02_book_metadata_editor.png

02_book_metadata_editor.png

**02_book_metadata_editor.png** ![02_book_metadata_editor.png](https://git.zombor.net/attachments/d7ce93db-26c9-4e63-bce4-5070181c030f)
Author
Owner

03_settings_match_weights.png

03_settings_match_weights.png

**03_settings_match_weights.png** ![03_settings_match_weights.png](https://git.zombor.net/attachments/85336c81-6b03-45b3-8cd8-c3c395fb933b)
Author
Owner

Security Review — bookshelf-o0m9 (PR #779)

Scope: Removal of obsolete Goodreads/Amazon/Audible provider UI, match-weight fields, and score contribution fields.

Findings

[MINOR] internal/settings/match_weights_handler.go:34 — stale "24 weight fields" comment
The handler docstring still says "all 24 weight fields" after the removal of the 5 Goodreads/Amazon/Audible fields. The live ErrIncompleteWeights message in match_weights.go was correctly updated to "all 19 weight fields", but this comment was missed. No security impact (the enforcement is in decodeMatchWeightsInput, not the comment), but a reviewer reading the handler may be confused about the actual acceptance criteria.


Security-specific checks (all clean)

  • Orphaned route registrations: None. All 4 match-weights routes (GET/PUT/DELETE /settings/match-weights, POST /settings/match-weights/recalculate) remain registered in internal/settings/routes.go and are all wrapped with adminRequired. The removal did not touch routes.
  • Auth/permission gap on retained endpoints: No retained settings endpoint lost its adminRequired guard. The SaveMatchWeightsHandler/ResetMatchWeightsHandler/RecalcMatchScoresHandler wiring is unchanged.
  • No destructive migration: Zero .sql files changed. The goodreads_rating, amazon_rating, audible_rating, goodreads_review_count, amazon_review_count, goodreads_rating_locked, etc. columns remain untouched on book_metadata — the removal is read/display-layer only. Existing data is safe.
  • No new surface introduced: This is a pure deletion. No new endpoints, handlers, or auth boundaries were added.
  • No secrets exposed: None.
  • Retained DTOs: internal/books/dto.go still carries GoodreadsID, AudibleID, AmazonASIN, GoodreadsRating, AmazonRating, AudibleRating fields (these are the book-metadata edit form fields which were intentionally NOT removed in this PR — the template still renders them for the metadata editor). This is consistent; only the rating display and match-score contribution of those providers was removed.

REVIEW VERDICT: 0 blocker, 0 major, 1 minor

## Security Review — bookshelf-o0m9 (PR #779) **Scope:** Removal of obsolete Goodreads/Amazon/Audible provider UI, match-weight fields, and score contribution fields. ### Findings [MINOR] internal/settings/match_weights_handler.go:34 — stale "24 weight fields" comment The handler docstring still says "all 24 weight fields" after the removal of the 5 Goodreads/Amazon/Audible fields. The live `ErrIncompleteWeights` message in `match_weights.go` was correctly updated to "all 19 weight fields", but this comment was missed. No security impact (the enforcement is in `decodeMatchWeightsInput`, not the comment), but a reviewer reading the handler may be confused about the actual acceptance criteria. --- ### Security-specific checks (all clean) - **Orphaned route registrations:** None. All 4 match-weights routes (`GET/PUT/DELETE /settings/match-weights`, `POST /settings/match-weights/recalculate`) remain registered in `internal/settings/routes.go` and are all wrapped with `adminRequired`. The removal did not touch routes. - **Auth/permission gap on retained endpoints:** No retained settings endpoint lost its `adminRequired` guard. The `SaveMatchWeightsHandler`/`ResetMatchWeightsHandler`/`RecalcMatchScoresHandler` wiring is unchanged. - **No destructive migration:** Zero `.sql` files changed. The `goodreads_rating`, `amazon_rating`, `audible_rating`, `goodreads_review_count`, `amazon_review_count`, `goodreads_rating_locked`, etc. columns remain untouched on `book_metadata` — the removal is read/display-layer only. Existing data is safe. - **No new surface introduced:** This is a pure deletion. No new endpoints, handlers, or auth boundaries were added. - **No secrets exposed:** None. - **Retained DTOs:** `internal/books/dto.go` still carries `GoodreadsID`, `AudibleID`, `AmazonASIN`, `GoodreadsRating`, `AmazonRating`, `AudibleRating` fields (these are the book-metadata edit form fields which were intentionally NOT removed in this PR — the template still renders them for the metadata editor). This is consistent; only the rating *display* and match-score *contribution* of those providers was removed. --- REVIEW VERDICT: 0 blocker, 0 major, 1 minor
Author
Owner

UI Review — PR #779 (bookshelf-o0m9)

Screenshot observations

01_book_detail.png — Book detail (Details tab): Provider ratings row shows HARDCOVER 4.70 / GOOGLE 4.50 / OPEN LIBRARY 4.20. No Goodreads, Amazon, or Audible badge present. Spacing and alignment of the ratings row look clean. No orphaned container. Looks correct.

02_book_metadata_editor.png — Edit Metadata tab: The "PROVIDER IDS & RATINGS" accordion section is rendered collapsed (not expanded). The main fields above it (title, authors, ISBNs, series, description, age/content rating) are all well-aligned using canonical metadata-field rows. However, because the accordion is collapsed the screenshot does NOT show whether the Goodreads Rating / Amazon Rating / Audible Rating input fields inside it were actually removed. The source confirms they were NOT removed (see finding below).

03_settings_match_weights.png — Named to verify "Settings -> Metadata" match-weights removal, but the screenshot shows the Email Settings tab, not the Metadata tab. The Metadata/match-weights panel is never rendered in this screenshot. This is a wrong-tab capture — the required rendered proof of the match-weights removal is absent from the PR screenshots.

04_series_show.png — Series detail: badges show "3 BOOKS", "0/3 READ", "4.7★ AVG. HARDCOVER" only. AvgGoodreads badge is gone. No orphaned container. Clean.


Findings

[MAJOR] templates/pages/books_show.html:1330-1387 — Goodreads/Amazon/Audible rating inputs not removed from the metadata editor (Edit Metadata -> Provider IDs & Ratings accordion)
The bead scope says to remove the Goodreads/Amazon/Audible rating displays. The provider-ratings display row on the book detail page was correctly removed (lines 228-246 deleted). The Goodreads ID field was correctly removed (lines 1213-1231 deleted). But the three matching rating-input fields inside the Edit Metadata accordion — "Goodreads Rating", "Amazon Rating", "Audible Rating" (with their lock buttons) — remain at lines 1330-1387 of the branch. These are editable inputs for dead-provider data that can never be refreshed. The comment at line 1330 still reads: Row: Goodreads rating · Amazon rating · Audible rating. Fix: remove that entire metadata-row div block (lines 1330-1387).

[MAJOR] templates/pages/books_show.html — Screenshot 02 does not show the expanded "Provider IDs & Ratings" section, so the above issue was not caught by the screenshot. The accordion must be captured in its open state to verify the provider-rating inputs are actually gone.

[MINOR] templates/pages/settings_shell.html — Screenshot 03 (named 03_settings_match_weights.png) shows the Email tab, not the Metadata tab. The Settings -> Metadata panel with the match-weights grid is never visible. A re-capture at /settings/metadata is needed to visually confirm the Goodreads/Amazon/Audible weight cards are absent.


REVIEW VERDICT: 0 blocker, 2 major, 1 minor

## UI Review — PR #779 (bookshelf-o0m9) ### Screenshot observations **01_book_detail.png** — Book detail (Details tab): Provider ratings row shows HARDCOVER 4.70 / GOOGLE 4.50 / OPEN LIBRARY 4.20. No Goodreads, Amazon, or Audible badge present. Spacing and alignment of the ratings row look clean. No orphaned container. Looks correct. **02_book_metadata_editor.png** — Edit Metadata tab: The "PROVIDER IDS & RATINGS" accordion section is rendered collapsed (not expanded). The main fields above it (title, authors, ISBNs, series, description, age/content rating) are all well-aligned using canonical metadata-field rows. However, because the accordion is collapsed the screenshot does NOT show whether the Goodreads Rating / Amazon Rating / Audible Rating input fields inside it were actually removed. The source confirms they were NOT removed (see finding below). **03_settings_match_weights.png** — Named to verify "Settings -> Metadata" match-weights removal, but the screenshot shows the Email Settings tab, not the Metadata tab. The Metadata/match-weights panel is never rendered in this screenshot. This is a wrong-tab capture — the required rendered proof of the match-weights removal is absent from the PR screenshots. **04_series_show.png** — Series detail: badges show "3 BOOKS", "0/3 READ", "4.7★ AVG. HARDCOVER" only. AvgGoodreads badge is gone. No orphaned container. Clean. --- ### Findings [MAJOR] templates/pages/books_show.html:1330-1387 — Goodreads/Amazon/Audible rating inputs not removed from the metadata editor (Edit Metadata -> Provider IDs & Ratings accordion) The bead scope says to remove the Goodreads/Amazon/Audible rating displays. The provider-ratings display row on the book detail page was correctly removed (lines 228-246 deleted). The Goodreads ID field was correctly removed (lines 1213-1231 deleted). But the three matching rating-input fields inside the Edit Metadata accordion — "Goodreads Rating", "Amazon Rating", "Audible Rating" (with their lock buttons) — remain at lines 1330-1387 of the branch. These are editable inputs for dead-provider data that can never be refreshed. The comment at line 1330 still reads: Row: Goodreads rating · Amazon rating · Audible rating. Fix: remove that entire metadata-row div block (lines 1330-1387). [MAJOR] templates/pages/books_show.html — Screenshot 02 does not show the expanded "Provider IDs & Ratings" section, so the above issue was not caught by the screenshot. The accordion must be captured in its open state to verify the provider-rating inputs are actually gone. [MINOR] templates/pages/settings_shell.html — Screenshot 03 (named 03_settings_match_weights.png) shows the Email tab, not the Metadata tab. The Settings -> Metadata panel with the match-weights grid is never visible. A re-capture at /settings/metadata is needed to visually confirm the Goodreads/Amazon/Audible weight cards are absent. --- REVIEW VERDICT: 0 blocker, 2 major, 1 minor
Author
Owner

Code Review — bookshelf-o0m9 (PR #779)

Phase 0: DEMO Verification

No runnable DEMO block was present in this PR. This is a UI removal/cleanup task. CI is green (SHA c07bdff9), which is the behavioral source of truth. Screenshots are posted via PR comments (01_book_detail.png, 02_book_metadata_editor.png, 03_settings_match_weights.png, 04_series_detail.png). Proceeding on CI-green + screenshot evidence.

Phase 1: Spec Compliance

Approach taken: option (b) — trim dead-provider cards from the match-weights feature (feature retained, Goodreads/Amazon/Audible cards removed). Match-weights feature (controller, routes, handler, recalculate) all retained — confirmed load-bearing for real providers.

Spec items confirmed done:

  • Match-weights table: Goodreads/Amazon/Audible weight cards removed from all 3 sections (per-library override, per-library defaults, global) in templates/pages/settings_shell.html
  • books_show.html: Goodreads/Amazon/Audible provider rating display blocks removed; Goodreads ID input removed
  • series_show.html: AvgGoodreads badge removed; AvgHardcover retained
  • ProviderRatings struct: Goodreads/Amazon/Audible fields removed
  • MatchWeights struct: 24→19 fields; DefaultMatchWeights total 87→79 (arithmetic verified)
  • MatchScoreInput: 10 dead fields removed; score calculation updated
  • DB columns untouched (confirmed: no changes to internal/db/ in diff)
  • Google/Hardcover/OpenLibrary displays, IDs, and weights retained
  • Goodreads/Amazon/Audible edit inputs in the metadata lock section retained (these persist to Grimmory-compat DB columns — correct)

Phase 2: Code Quality

[MINOR] templates/pages/books_show.html:1180 — stale section comment
The comment {{/* Row: Google ID · Hardcover ID · Goodreads ID */}} still references "Goodreads ID" even though the Goodreads ID input was removed by this PR. The row now only contains Google Books ID and Hardcover ID. No correctness impact.

No other issues found:

  • TotalWeight() denominator update (87→79): arithmetic correct (removed 2+1+2+1+2=8 points)
  • ErrIncompleteWeights message updated to "all 19 weight fields" and verified against 19 pointer fields in matchWeightsInput
  • Test denominators in match_score_test.go all updated from 87 to 79 — correct
  • match_score_store_test.go: removed assertions for Goodreads/Amazon/Audible mapping (correct — the domain struct fields were removed); sqlc row fields still populated in the test fixture to confirm they are silently ignored
  • series/store.go: goodreads_rating column removed from booksInSeriesBaseQuery and scan target — correct; no other scan-target misalignment
  • No inline style= attributes introduced
  • No committed screenshot scripts
  • No dangling references: app.js still registers match-weights-settings (controller file retained), templates reference no removed Go struct fields

REVIEW VERDICT: 0 blocker, 0 major, 1 minor

## Code Review — bookshelf-o0m9 (PR #779) ### Phase 0: DEMO Verification No runnable DEMO block was present in this PR. This is a UI removal/cleanup task. CI is green (SHA c07bdff9), which is the behavioral source of truth. Screenshots are posted via PR comments (01_book_detail.png, 02_book_metadata_editor.png, 03_settings_match_weights.png, 04_series_detail.png). Proceeding on CI-green + screenshot evidence. ### Phase 1: Spec Compliance Approach taken: option (b) — trim dead-provider cards from the match-weights feature (feature retained, Goodreads/Amazon/Audible cards removed). Match-weights feature (controller, routes, handler, recalculate) all retained — confirmed load-bearing for real providers. **Spec items confirmed done:** - Match-weights table: Goodreads/Amazon/Audible weight cards removed from all 3 sections (per-library override, per-library defaults, global) in `templates/pages/settings_shell.html` - `books_show.html`: Goodreads/Amazon/Audible provider rating display blocks removed; Goodreads ID input removed - `series_show.html`: AvgGoodreads badge removed; AvgHardcover retained - `ProviderRatings` struct: Goodreads/Amazon/Audible fields removed - `MatchWeights` struct: 24→19 fields; `DefaultMatchWeights` total 87→79 (arithmetic verified) - `MatchScoreInput`: 10 dead fields removed; score calculation updated - DB columns untouched (confirmed: no changes to `internal/db/` in diff) - Google/Hardcover/OpenLibrary displays, IDs, and weights retained - Goodreads/Amazon/Audible edit inputs in the metadata lock section retained (these persist to Grimmory-compat DB columns — correct) ### Phase 2: Code Quality [MINOR] `templates/pages/books_show.html:1180` — stale section comment The comment `{{/* Row: Google ID · Hardcover ID · Goodreads ID */}}` still references "Goodreads ID" even though the Goodreads ID input was removed by this PR. The row now only contains Google Books ID and Hardcover ID. No correctness impact. No other issues found: - `TotalWeight()` denominator update (87→79): arithmetic correct (removed 2+1+2+1+2=8 points) - `ErrIncompleteWeights` message updated to "all 19 weight fields" and verified against 19 pointer fields in `matchWeightsInput` - Test denominators in `match_score_test.go` all updated from 87 to 79 — correct - `match_score_store_test.go`: removed assertions for Goodreads/Amazon/Audible mapping (correct — the domain struct fields were removed); sqlc row fields still populated in the test fixture to confirm they are silently ignored - `series/store.go`: `goodreads_rating` column removed from `booksInSeriesBaseQuery` and scan target — correct; no other scan-target misalignment - No inline `style=` attributes introduced - No committed screenshot scripts - No dangling references: `app.js` still registers `match-weights-settings` (controller file retained), templates reference no removed Go struct fields REVIEW VERDICT: 0 blocker, 0 major, 1 minor
fix(o0m9): remove Goodreads/Amazon/Audible rating edit inputs; fix stale comments
All checks were successful
/ JS Unit Tests (pull_request) Successful in 1m32s
/ Lint (pull_request) Successful in 2m37s
/ E2E API (pull_request) Successful in 2m13s
/ Hugo build (pull_request) Successful in 29s
/ Integration (pull_request) Successful in 3m16s
/ Test (pull_request) Successful in 3m41s
/ E2E Browser (pull_request) Successful in 3m27s
d05f5a548a
- Remove dead-provider rating edit inputs (Goodreads, Amazon, Audible) from
  Edit Metadata "Provider IDs & Ratings" accordion (UI MAJOR finding on PR #779).
  ID fields for these providers remain (they can still be set manually).
- Remove corresponding DTO fields (GoodreadsRating, AmazonRating, AudibleRating
  from ProviderIDsRequest), handler parse calls, and store write paths since the
  form no longer submits these values.
- Fix stale comment: row header "Google ID · Hardcover ID · Goodreads ID"
  updated to "Google ID · Hardcover ID" (Goodreads ID was already removed).
- Fix docstring in match_weights_handler.go: 24 → 19 weight fields.
- Update tests to remove assertions for the removed fields.
- Grimmory-compat DB columns (goodreads_rating etc.) remain untouched;
  the match-weights feature is fully retained.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Author
Owner

Corrected Screenshots — fix commit d05f5a54

Fix applied: Goodreads Rating, Amazon Rating, and Audible Rating edit inputs removed from Edit Metadata accordion.

(a) Edit Metadata accordion — Provider IDs & Ratings section (open)

Only real providers remain: Hardcover Rating, Google Books Rating, Open Library Rating. Dead-provider rating inputs gone.

Edit Metadata Provider IDs & Ratings

(b) Settings → Metadata tab — match-weights grid

Match-weights grid shows no Goodreads/Amazon/Audible weight cards (never had them — those were match-weight fields already removed in the prior commit). Grid is intact.

Settings Metadata Match Weights

## Corrected Screenshots — fix commit d05f5a54 Fix applied: Goodreads Rating, Amazon Rating, and Audible Rating edit inputs removed from Edit Metadata accordion. ### (a) Edit Metadata accordion — Provider IDs & Ratings section (open) Only real providers remain: Hardcover Rating, Google Books Rating, Open Library Rating. Dead-provider rating inputs gone. ![Edit Metadata Provider IDs & Ratings](https://git.zombor.net/attachments/2e0fbf6a-a952-4dda-a2be-9ec2be8e1ace) ### (b) Settings → Metadata tab — match-weights grid Match-weights grid shows no Goodreads/Amazon/Audible weight cards (never had them — those were match-weight fields already removed in the prior commit). Grid is intact. ![Settings Metadata Match Weights](https://git.zombor.net/attachments/4bbf508a-9841-4373-88b8-0cb09825408c)
Author
Owner

Security Review — PR #779 (bookshelf-o0m9) — round 2

Reviewing git diff origin/main...origin/bd-bookshelf-o0m9 (HEAD d05f5a54). Scope: Goodreads/Amazon/Audible rating edit inputs, DTO/handler/store write path removal; series AvgGoodreads removal; settings MatchWeights field removal.

Checks performed

  1. No destructive migrationgit diff origin/main...origin/bd-bookshelf-o0m9 -- internal/db/ produced no output. The Grimmory DB columns (goodreads_rating, amazon_rating, audible_rating, goodreads_rating_locked, etc.) are untouched; the PR only removes the Go code that read/wrote them from the form. The goodreads_id, amazon_rating, audible_rating columns remain in the schema.

  2. No orphaned handler pathformToProviderIDsRequest terminates cleanly after OpenLibraryRating; no dead if p.GoodreadsRating ... branches remain in metadata_handler.go. The UpdateProviderIDs store function still writes GoodreadsID, AudibleID, AmazonASIN (IDs are retained — only the rating fields are gone from the write path).

  3. Metadata-update endpoint still works for retained fieldsProviderIDsRequest retains HardcoverRating, GoogleBooksRating, OpenLibraryRating; UpdateProviderIDs maps all three correctly to sql.NullFloat64. No auth or validation check was accidentally dropped.

  4. Lock-toggle endpoint fully intactlockableFields still contains "goodreads_rating", "amazon_rating", "audible_rating" entries, and the switch in ToggleFieldLock dispatches them correctly. Users who previously locked these fields can still unlock them via the lock-toggle endpoint even though the edit inputs are no longer rendered.

  5. MatchWeights consistencyMatchWeights struct, matchWeightsInput, decodeMatchWeightsInput ptrs slice, Validate() fields slice, and TotalWeight() all have the same 19 fields. The ErrIncompleteWeights error string, the handler comment, and the test fixture JSON are all updated to 19. No partial-update inconsistency.

  6. No secrets — no credentials, tokens, or API keys in the diff.

  7. No new attack surface — surface is reduced (fewer form fields accepted). JSON tags for the removed fields are simply absent; unknown form keys are silently ignored by setFloat/setStr (returns nil, nil), so no 500 risk from stale clients submitting the old fields.

Findings

None.

REVIEW VERDICT: 0 blocker, 0 major, 0 minor

## Security Review — PR #779 (bookshelf-o0m9) — round 2 Reviewing `git diff origin/main...origin/bd-bookshelf-o0m9` (HEAD d05f5a54). Scope: Goodreads/Amazon/Audible rating edit inputs, DTO/handler/store write path removal; series `AvgGoodreads` removal; settings `MatchWeights` field removal. ### Checks performed 1. **No destructive migration** — `git diff origin/main...origin/bd-bookshelf-o0m9 -- internal/db/` produced no output. The Grimmory DB columns (`goodreads_rating`, `amazon_rating`, `audible_rating`, `goodreads_rating_locked`, etc.) are untouched; the PR only removes the Go code that read/wrote them from the form. The `goodreads_id`, `amazon_rating`, `audible_rating` columns remain in the schema. 2. **No orphaned handler path** — `formToProviderIDsRequest` terminates cleanly after `OpenLibraryRating`; no dead `if p.GoodreadsRating ...` branches remain in `metadata_handler.go`. The `UpdateProviderIDs` store function still writes `GoodreadsID`, `AudibleID`, `AmazonASIN` (IDs are retained — only the rating fields are gone from the write path). 3. **Metadata-update endpoint still works for retained fields** — `ProviderIDsRequest` retains `HardcoverRating`, `GoogleBooksRating`, `OpenLibraryRating`; `UpdateProviderIDs` maps all three correctly to `sql.NullFloat64`. No auth or validation check was accidentally dropped. 4. **Lock-toggle endpoint fully intact** — `lockableFields` still contains `"goodreads_rating"`, `"amazon_rating"`, `"audible_rating"` entries, and the switch in `ToggleFieldLock` dispatches them correctly. Users who previously locked these fields can still unlock them via the lock-toggle endpoint even though the edit inputs are no longer rendered. 5. **MatchWeights consistency** — `MatchWeights` struct, `matchWeightsInput`, `decodeMatchWeightsInput` ptrs slice, `Validate()` fields slice, and `TotalWeight()` all have the same 19 fields. The `ErrIncompleteWeights` error string, the handler comment, and the test fixture JSON are all updated to 19. No partial-update inconsistency. 6. **No secrets** — no credentials, tokens, or API keys in the diff. 7. **No new attack surface** — surface is reduced (fewer form fields accepted). JSON tags for the removed fields are simply absent; unknown form keys are silently ignored by `setFloat`/`setStr` (returns `nil, nil`), so no 500 risk from stale clients submitting the old fields. ### Findings None. REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Author
Owner

UI Re-Review — PR #779 (bookshelf-o0m9) — fix commit d05f5a54

Verifying the two prior MAJORs against the corrected screenshots in comment 9959.

Screenshot observations

Edit Metadata accordion (open) — attachment 2e0fbf6a: The accordion is rendered OPEN. The "PROVIDER IDS & RATINGS" section shows the legend title and all fields are visible. The rating row at the bottom contains exactly three inputs: Hardcover Rating, Google Books Rating, Open Library Rating. No Goodreads Rating, Amazon Rating, or Audible Rating input is present. Audible ID and Amazon ASIN identifier fields correctly remain (these are provider IDs, not rating inputs — correct). The layout is clean and uses canonical .metadata-field + .metadata-field-input rows throughout. No orphaned rows or containers.

Settings → Metadata tab — attachment 4bbf508a: The Metadata tab is open (not Email). The match-weights grid is visible. Scanning the grid: no Goodreads, Amazon, or Audible weight cards present. Real-provider cards (Google Books, Open Library, Hardcover, etc.) are present. Grid layout looks intact.

Source cross-check

  • grep -i "goodreads.*rating\|amazon.*rating\|audible.*rating" on templates/pages/books_show.html at the branch HEAD returns empty — no rating inputs for dead providers remain.
  • grep -i "goodreads\|amazon\|audible" on templates/pages/settings_shell.html returns empty — no dead-provider references.
  • No style= inline attributes found in either changed template.
  • No committed screenshot scripts found in the branch diff.

Prior MAJOR resolution

Both prior MAJORs are resolved:

  • [MAJOR] #1 (Goodreads/Amazon/Audible rating inputs not removed from Edit Metadata accordion): The fix commit d05f5a54 removed these inputs. The open accordion screenshot confirms their absence. The template source confirms no such inputs exist in the branch.
  • [MAJOR] #2 / MINOR (collapsed accordion screenshot + wrong-tab settings screenshot): Both are corrected. The new screenshots show the accordion open and the Settings Metadata tab (not Email).

Findings

No new findings.

REVIEW VERDICT: 0 blocker, 0 major, 0 minor

## UI Re-Review — PR #779 (bookshelf-o0m9) — fix commit d05f5a54 Verifying the two prior MAJORs against the corrected screenshots in comment 9959. ### Screenshot observations **Edit Metadata accordion (open) — attachment 2e0fbf6a:** The accordion is rendered OPEN. The "PROVIDER IDS & RATINGS" section shows the legend title and all fields are visible. The rating row at the bottom contains exactly three inputs: Hardcover Rating, Google Books Rating, Open Library Rating. No Goodreads Rating, Amazon Rating, or Audible Rating input is present. Audible ID and Amazon ASIN identifier fields correctly remain (these are provider IDs, not rating inputs — correct). The layout is clean and uses canonical `.metadata-field` + `.metadata-field-input` rows throughout. No orphaned rows or containers. **Settings → Metadata tab — attachment 4bbf508a:** The Metadata tab is open (not Email). The match-weights grid is visible. Scanning the grid: no Goodreads, Amazon, or Audible weight cards present. Real-provider cards (Google Books, Open Library, Hardcover, etc.) are present. Grid layout looks intact. ### Source cross-check - `grep -i "goodreads.*rating\|amazon.*rating\|audible.*rating"` on `templates/pages/books_show.html` at the branch HEAD returns empty — no rating inputs for dead providers remain. - `grep -i "goodreads\|amazon\|audible"` on `templates/pages/settings_shell.html` returns empty — no dead-provider references. - No `style=` inline attributes found in either changed template. - No committed screenshot scripts found in the branch diff. ### Prior MAJOR resolution Both prior MAJORs are resolved: - **[MAJOR] #1** (Goodreads/Amazon/Audible rating inputs not removed from Edit Metadata accordion): The fix commit d05f5a54 removed these inputs. The open accordion screenshot confirms their absence. The template source confirms no such inputs exist in the branch. - **[MAJOR] #2 / MINOR** (collapsed accordion screenshot + wrong-tab settings screenshot): Both are corrected. The new screenshots show the accordion open and the Settings Metadata tab (not Email). ### Findings No new findings. REVIEW VERDICT: 0 blocker, 0 major, 0 minor
Author
Owner

CODE REVIEW — bookshelf-o0m9 (PR #779)

Diff reviewed: git diff origin/main...origin/bd-bookshelf-o0m9 HEAD d05f5a54.


Phase 0: DEMO — N/A (UI/cleanup bead, no DEMO block required per bead type)


Phase 1: Spec Compliance

All five spec requirements verified:

  1. Dead-provider rating edit inputs removed from books_show.html — Goodreads Rating, Amazon Rating, Audible Rating <input> rows removed from the Edit Metadata accordion. Confirmed: no provider_goodreads_rating, provider_amazon_rating, provider_audible_rating in template. ✓

  2. Real providers retained — Google Books, Open Library, Hardcover, ISBN inputs remain. ✓

  3. DTO / handler / store write pathGoodreadsRating, AmazonRating, AudibleRating removed from ProviderIDsRequest, formToProviderIDsRequest, and UpdateProviderIDs. ✓ (verified no dangling rating write references)

  4. DB columns untouched — no migration files changed. DB read paths (GetProviderRatings, buildMetaFromRow, ratings JSON map) preserve Goodreads/Amazon/Audible columns. ✓

  5. Match weights — 5 dead-provider weight fields removed from MatchWeights, matchWeightsInput, TotalWeight, decodeMatchWeightsInput, Validate, DefaultMatchWeights; error message updated 24→19; settings UI cards removed from settings_shell.html. ✓


Phase 2: Findings

[BLOCKER] internal/books/metadata_store.go:592 + internal/books/metadata_handler.go:406 — Goodreads ID silently destroyed on every provider-form save

books_show.html had the Goodreads ID edit input (name="provider_goodreads_id") removed in this PR (it was present on main at line 1235). However, formToProviderIDsRequest still calls setStr("provider_goodreads_id") (handler.go:406), which returns nil when the key is absent from the submitted form. UpdateProviderIDs then maps that nil to nullStr(nil)sql.NullString{Valid: false} and writes it to book_metadata.goodreads_id (store.go:592). Any user who saves the Provider IDs accordion will silently NULL out their stored Goodreads ID with no warning or way to recover it.

The Goodreads ID write path (GoodreadsID in ProviderIDsRequest, the setStr call, and the nullStr(req.GoodreadsID) in the store) must either:

  • retain the input field in the template (if Goodreads ID editing is still desired), OR
  • remove GoodreadsID from the write path entirely (DTO field, handler setStr call, store p.GoodreadsID = nullStr(...) call, and the lockableFields allow-list entry "goodreads_id") so the field is read-only from this form. The DB column stays; only the edit surface changes.

[MINOR] internal/settings/match_weights_handler_test.go:127 — stale comment

Comment reads "the other 23 fields are absent" but the payload now has 19 total fields, so it should say "the other 18 fields".


REVIEW VERDICT: 1 blocker, 0 major, 1 minor

## CODE REVIEW — bookshelf-o0m9 (PR #779) Diff reviewed: `git diff origin/main...origin/bd-bookshelf-o0m9` HEAD d05f5a54. --- ### Phase 0: DEMO — N/A (UI/cleanup bead, no DEMO block required per bead type) --- ### Phase 1: Spec Compliance All five spec requirements verified: 1. **Dead-provider rating edit inputs removed from `books_show.html`** — Goodreads Rating, Amazon Rating, Audible Rating `<input>` rows removed from the Edit Metadata accordion. Confirmed: no `provider_goodreads_rating`, `provider_amazon_rating`, `provider_audible_rating` in template. ✓ 2. **Real providers retained** — Google Books, Open Library, Hardcover, ISBN inputs remain. ✓ 3. **DTO / handler / store write path** — `GoodreadsRating`, `AmazonRating`, `AudibleRating` removed from `ProviderIDsRequest`, `formToProviderIDsRequest`, and `UpdateProviderIDs`. ✓ (verified no dangling *rating* write references) 4. **DB columns untouched** — no migration files changed. DB read paths (`GetProviderRatings`, `buildMetaFromRow`, ratings JSON map) preserve Goodreads/Amazon/Audible columns. ✓ 5. **Match weights** — 5 dead-provider weight fields removed from `MatchWeights`, `matchWeightsInput`, `TotalWeight`, `decodeMatchWeightsInput`, `Validate`, `DefaultMatchWeights`; error message updated 24→19; settings UI cards removed from `settings_shell.html`. ✓ --- ### Phase 2: Findings **[BLOCKER] `internal/books/metadata_store.go`:592 + `internal/books/metadata_handler.go`:406 — Goodreads ID silently destroyed on every provider-form save** `books_show.html` had the Goodreads ID edit input (`name="provider_goodreads_id"`) removed in this PR (it was present on `main` at line 1235). However, `formToProviderIDsRequest` still calls `setStr("provider_goodreads_id")` (handler.go:406), which returns `nil` when the key is absent from the submitted form. `UpdateProviderIDs` then maps that `nil` to `nullStr(nil)` → `sql.NullString{Valid: false}` and writes it to `book_metadata.goodreads_id` (store.go:592). Any user who saves the Provider IDs accordion will silently NULL out their stored Goodreads ID with no warning or way to recover it. The Goodreads ID write path (`GoodreadsID` in `ProviderIDsRequest`, the `setStr` call, and the `nullStr(req.GoodreadsID)` in the store) must either: - retain the input field in the template (if Goodreads ID editing is still desired), OR - remove `GoodreadsID` from the write path entirely (DTO field, handler `setStr` call, store `p.GoodreadsID = nullStr(...)` call, and the `lockableFields` allow-list entry `"goodreads_id"`) so the field is read-only from this form. The DB column stays; only the edit surface changes. **[MINOR] `internal/settings/match_weights_handler_test.go`:127 — stale comment** Comment reads `"the other 23 fields are absent"` but the payload now has 19 total fields, so it should say `"the other 18 fields"`. --- REVIEW VERDICT: 1 blocker, 0 major, 1 minor
fix(books): remove goodreads_id from Provider-IDs write path to prevent data loss
All checks were successful
/ Hugo build (pull_request) Successful in 19s
/ JS Unit Tests (pull_request) Successful in 1m9s
/ E2E API (pull_request) Successful in 1m42s
/ Lint (pull_request) Successful in 1m50s
/ E2E Browser (pull_request) Successful in 2m12s
/ Integration (pull_request) Successful in 2m46s
/ Test (pull_request) Successful in 2m49s
233ca4c9ec
The goodreads_id input was removed from the UI form but the write path
still read it, causing UpdateBookProviderIDs to always pass NULL for
goodreads_id — silently destroying the user's stored Goodreads ID on
every Provider IDs accordion save.

Fix (option b — make goodreads_id read-only via this form):
- Remove GoodreadsID from ProviderIDsRequest DTO
- Remove provider_goodreads_id from providerIDMaxLen map
- Remove setStr("provider_goodreads_id") call in formToProviderIDsRequest
- Remove p.GoodreadsID assignment in UpdateProviderIDs store func
- Remove bm.goodreads_id SET clause from UpdateBookProviderIDs SQL query
- Remove "goodreads_id" from lockableFields (no longer UI-editable)
- Remove case "goodreads_id" from ToggleFieldLock switch (unreachable)
- Update all tests to remove goodreads_id from write/lock assertions
- Fix stale "other 23 fields" comment → "other 18 fields" in
  match_weights_handler_test.go

The DB column, read path (GetBookProviderIDsRow→MetadataRow), and
enrichment/lock-setter wiring are all preserved unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Author
Owner

CODE REVIEW: BLOCKER FIX VERIFICATION — PR #779 (bookshelf-o0m9)

Focused re-review of the BLOCKER fix commit. CI is green. Verified on origin/bd-bookshelf-o0m9 HEAD.


Phase 0: DEMO

This is a focused code-correctness re-review of a BLOCKER fix (not a full feature demo). No standalone DEMO block applies — correctness verified by static analysis of the diff below.


Findings

BLOCKER resolved — CONFIRMED.

  1. ProviderIDsRequest struct (internal/books/dto.go:105) — GoodreadsID *string field is absent. Comment at line 103 documents the intent explicitly.

  2. formToProviderIDsRequest (internal/books/metadata_handler.go:370) — no setStr("provider_goodreads_id") call. Lines 400-412 map only Google, Hardcover, ComicVine, Audible, ASIN.

  3. UpdateBookProviderIDsParams (internal/db/sqlc/metadata_extra.go:109) — GoodreadsID field absent. The UPDATE SQL SET clause (lines 136-146) has no bm.goodreads_id = ? entry and no corresponding arg in ExecContext. The DB column cannot be NULLed by the form-save path.

  4. DB column preservedGetBookProviderIDs SELECT (metadata_extra.go:48) still reads bm.goodreads_id and bm.goodreads_id_locked. No migration touching the column. Read path intact.

  5. Enrichment write preservedUpsertBookMetadata / metadataToUpsertParams path in metadata_store.go is unchanged for goodreads_id; enrichment can still populate the column.

  6. lockableFields (internal/books/metadata_store.go) — "goodreads_id" removed from the allow-list map. IsValidLockField("goodreads_id") returns false. Correct.

  7. ToggleFieldLock switchcase "goodreads_id": removed. goodreads_rating case retained at line 746. No other field case disturbed.


[MINOR] internal/books/metadata_store.go:669 — SetGoodreadsIDLocked remains in ToggleFieldLockDeps struct but is now unreachable via the switch/lockableFields.
The field is never dispatched to (no case in the switch, not in lockableFields). It compiles cleanly, tests still populate it for completeness, and wire.go:336 still wires it. Dead code on the deps struct. No correctness or security impact. Could be removed in a follow-up cleanup, does not block merge.


Scope sanity

The diff correctly removes only Goodreads/Amazon/Audible dead-provider fields:

  • MatchWeights struct: 24 to 19 fields (removed GoodreadsRating, GoodreadsReviewCount, AmazonRating, AmazonReviewCount, AudibleRating)
  • MatchScoreInput: removed GoodreadsRating, AmazonRating, AudibleRating and related lock/review-count fields
  • ProviderRatings read path in store.go: removed Goodreads/Amazon/Audible
  • Template: removed display blocks and Goodreads ID input field
  • Weights feature itself is retained: MatchWeights, DefaultMatchWeights, SaveMatchWeightsHandler, ResetMatchWeightsHandler, all routes/deps preserved
  • Still-supported providers (Google Books, Open Library, Hardcover) untouched throughout

REVIEW VERDICT: 0 blocker, 0 major, 1 minor

APPROVED — original BLOCKER is fully resolved. The form-write path cannot NULL bm.goodreads_id. DB column, read path, and enrichment writers are all preserved. Weights feature retained. The sole minor finding (dead SetGoodreadsIDLocked struct field) does not block merge.

## CODE REVIEW: BLOCKER FIX VERIFICATION — PR #779 (bookshelf-o0m9) Focused re-review of the BLOCKER fix commit. CI is green. Verified on `origin/bd-bookshelf-o0m9` HEAD. --- ### Phase 0: DEMO This is a focused code-correctness re-review of a BLOCKER fix (not a full feature demo). No standalone DEMO block applies — correctness verified by static analysis of the diff below. --- ### Findings **BLOCKER resolved — CONFIRMED.** 1. `ProviderIDsRequest` struct (`internal/books/dto.go:105`) — `GoodreadsID *string` field is absent. Comment at line 103 documents the intent explicitly. 2. `formToProviderIDsRequest` (`internal/books/metadata_handler.go:370`) — no `setStr("provider_goodreads_id")` call. Lines 400-412 map only Google, Hardcover, ComicVine, Audible, ASIN. 3. `UpdateBookProviderIDsParams` (`internal/db/sqlc/metadata_extra.go:109`) — `GoodreadsID` field absent. The UPDATE SQL SET clause (lines 136-146) has no `bm.goodreads_id = ?` entry and no corresponding arg in `ExecContext`. The DB column cannot be NULLed by the form-save path. 4. **DB column preserved** — `GetBookProviderIDs` SELECT (metadata_extra.go:48) still reads `bm.goodreads_id` and `bm.goodreads_id_locked`. No migration touching the column. Read path intact. 5. **Enrichment write preserved** — `UpsertBookMetadata` / `metadataToUpsertParams` path in metadata_store.go is unchanged for goodreads_id; enrichment can still populate the column. 6. **lockableFields** (`internal/books/metadata_store.go`) — `"goodreads_id"` removed from the allow-list map. `IsValidLockField("goodreads_id")` returns false. Correct. 7. **ToggleFieldLock switch** — `case "goodreads_id":` removed. `goodreads_rating` case retained at line 746. No other field case disturbed. --- [MINOR] internal/books/metadata_store.go:669 — `SetGoodreadsIDLocked` remains in `ToggleFieldLockDeps` struct but is now unreachable via the switch/lockableFields. The field is never dispatched to (no case in the switch, not in lockableFields). It compiles cleanly, tests still populate it for completeness, and wire.go:336 still wires it. Dead code on the deps struct. No correctness or security impact. Could be removed in a follow-up cleanup, does not block merge. --- ### Scope sanity The diff correctly removes only Goodreads/Amazon/Audible dead-provider fields: - `MatchWeights` struct: 24 to 19 fields (removed GoodreadsRating, GoodreadsReviewCount, AmazonRating, AmazonReviewCount, AudibleRating) - `MatchScoreInput`: removed GoodreadsRating, AmazonRating, AudibleRating and related lock/review-count fields - `ProviderRatings` read path in store.go: removed Goodreads/Amazon/Audible - Template: removed display blocks and Goodreads ID input field - **Weights feature itself is retained**: MatchWeights, DefaultMatchWeights, SaveMatchWeightsHandler, ResetMatchWeightsHandler, all routes/deps preserved - Still-supported providers (Google Books, Open Library, Hardcover) untouched throughout --- REVIEW VERDICT: 0 blocker, 0 major, 1 minor **APPROVED** — original BLOCKER is fully resolved. The form-write path cannot NULL `bm.goodreads_id`. DB column, read path, and enrichment writers are all preserved. Weights feature retained. The sole minor finding (dead SetGoodreadsIDLocked struct field) does not block merge.
zombor force-pushed bd-bookshelf-o0m9 from 233ca4c9ec
All checks were successful
/ Hugo build (pull_request) Successful in 19s
/ JS Unit Tests (pull_request) Successful in 1m9s
/ E2E API (pull_request) Successful in 1m42s
/ Lint (pull_request) Successful in 1m50s
/ E2E Browser (pull_request) Successful in 2m12s
/ Integration (pull_request) Successful in 2m46s
/ Test (pull_request) Successful in 2m49s
to 55d593a363
All checks were successful
/ JS Unit Tests (pull_request) Successful in 1m32s
/ E2E Browser (pull_request) Successful in 1m54s
/ E2E API (pull_request) Successful in 2m1s
/ Lint (pull_request) Successful in 2m9s
/ Integration (pull_request) Successful in 2m47s
/ Test (pull_request) Successful in 2m51s
2026-06-25 19:00:25 +00:00
Compare
zombor merged commit 99cad926ea into main 2026-06-25 19:03:43 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
zombor/pergamum!779
No description provided.