v26.05.26.1 — thumb backfill, modal redesign, recovery sweep race-safety, artist view redesign, extension fixes #23

Merged
bvandeusen merged 0 commits from dev into main 2026-05-26 16:32:00 -04:00
bvandeusen commented 2026-05-26 16:31:08 -04:00 (Migrated from git.fabledsword.com)

Summary

20 commits on dev (5d4f223 → fb41b90). Adds a thumbnail-backfill UI, fixes three psycopg-65535-parameter latents, race-proofs two more SELECT-then-INSERT sites with DB unique constraints, redesigns the artist detail view, and ships HTTP-friendly Copy + a context-rich ErrorDetailModal.

Features

  • Thumbnail backfill — new backfill_thumbnails Celery task + POST /api/thumbnails/backfill + Pinia store + ThumbnailBackfillCard wired into MaintenancePanel. Three-predicate scan (NULL / missing on disk / corrupt magic bytes) re-enqueues generate_thumbnail for repair candidates.

  • Artist view redesign — sticky frosted ArtistHeader (matches TopNav blur recipe, hosts name + image/post stats + tab strip). Three lazy tabs: Posts (default when post_count > 0), Gallery (fallback), Management. ?tab= URL state, cross-artist store reset, document.title per artist.

  • ErrorDetailModal redesignrow prop carries the failure's task name / queue / target / duration / started / retries / worker / celery-id / args alongside the error message. Pre-block contrast fix (theme background token vs auto-derived surface-variant).

Fixes

  • psycopg 65535-parameter ceiling, three sitesrecover_interrupted_tasks, /api/import/retry-failed, /api/import/clear-stuck all folded their SELECT ids → UPDATE WHERE id IN (...) patterns into UPDATE … WHERE … RETURNING. The IN-list is impossible to overflow now regardless of row count.

  • ImageProvenance race + alembic 0021 — _apply_sidecar's existence-check + INSERT was racy; concurrent workers planted (image_record_id, post_id) duplicates that then broke .scalar_one_or_none() on every later deep-scan rederive (MultipleResultsFound). Migration dedupes existing rows + adds uq_image_provenance_image_post. Importer switched to savepoint + IntegrityError recovery.

  • Source per (artist, platform), not per post + alembic 0022 — filesystem importer was creating one Source row per imported post URL (Atole's artist page had 406 Sources where 1 was right). Migration collapses per-artist-per-platform groups to the campaign-URL canonical (or a sidecar:<platform>:<slug> synthetic with enabled=false), reparenting Posts + ImageProvenance and merging Post collisions. Importer now reuses the existing subscription Source.

  • Extension race-safetyExtensionService._find_or_create_artist + _find_or_create_source switched to the same savepoint + IntegrityError pattern. Two fast clicks of the extension's "Add as source" no longer 500 on the second one.

  • Copy button on plain HTTPnavigator.clipboard is gated by Secure Context (HTTPS-only). New copyText() helper falls back to document.execCommand('copy') via an off-screen textarea. Applied to ErrorDetailModal, ExtensionKeyBar, BrowserExtensionCard.

Migrations

  • 0021_image_provenance_unique — dedupe + UNIQUE(image_record_id, post_id).
  • 0022_source_per_artist_platform — collapse per-post Sources, reparent Posts + ImageProvenance, merge Post collisions.

Test plan

  • Deploy :latest, force-update app, worker, ml-worker, scheduler, web. Alembic auto-applies 0021 + 0022.
  • Verify Atole's artist page now shows 1 Source under Management → Subscriptions.
  • Verify the new artist view: Posts tab default when posts exist, Gallery fallback when zero, frosted header continuous with TopNav.
  • Settings → Maintenance → Browser extension: install button still works in Firefox.
  • Settings → Maintenance: new ThumbnailBackfillCard present in the grid.
  • System Activity → click an error: modal shows task context + crisp pre-block; Copy button works on plain HTTP.
  • Walk through the FC-3g verification runbook (extension install → cookies push → quick-add-source).

🤖 Generated with Claude Code

## Summary 20 commits on dev (`5d4f223 → fb41b90`). Adds a thumbnail-backfill UI, fixes three psycopg-65535-parameter latents, race-proofs two more SELECT-then-INSERT sites with DB unique constraints, redesigns the artist detail view, and ships HTTP-friendly Copy + a context-rich ErrorDetailModal. ### Features - **Thumbnail backfill** — new `backfill_thumbnails` Celery task + `POST /api/thumbnails/backfill` + Pinia store + ThumbnailBackfillCard wired into MaintenancePanel. Three-predicate scan (NULL / missing on disk / corrupt magic bytes) re-enqueues `generate_thumbnail` for repair candidates. - **Artist view redesign** — sticky frosted `ArtistHeader` (matches TopNav blur recipe, hosts name + image/post stats + tab strip). Three lazy tabs: Posts (default when `post_count > 0`), Gallery (fallback), Management. `?tab=` URL state, cross-artist store reset, `document.title` per artist. - **ErrorDetailModal redesign** — `row` prop carries the failure's task name / queue / target / duration / started / retries / worker / celery-id / args alongside the error message. Pre-block contrast fix (theme `background` token vs auto-derived `surface-variant`). ### Fixes - **psycopg 65535-parameter ceiling, three sites** — `recover_interrupted_tasks`, `/api/import/retry-failed`, `/api/import/clear-stuck` all folded their `SELECT ids → UPDATE WHERE id IN (...)` patterns into `UPDATE … WHERE … RETURNING`. The IN-list is impossible to overflow now regardless of row count. - **ImageProvenance race** + alembic 0021 — `_apply_sidecar`'s existence-check + INSERT was racy; concurrent workers planted `(image_record_id, post_id)` duplicates that then broke `.scalar_one_or_none()` on every later deep-scan rederive (MultipleResultsFound). Migration dedupes existing rows + adds `uq_image_provenance_image_post`. Importer switched to savepoint + IntegrityError recovery. - **Source per (artist, platform), not per post** + alembic 0022 — filesystem importer was creating one Source row per imported post URL (Atole's artist page had 406 Sources where 1 was right). Migration collapses per-artist-per-platform groups to the campaign-URL canonical (or a `sidecar:<platform>:<slug>` synthetic with `enabled=false`), reparenting Posts + ImageProvenance and merging Post collisions. Importer now reuses the existing subscription Source. - **Extension race-safety** — `ExtensionService._find_or_create_artist` + `_find_or_create_source` switched to the same savepoint + IntegrityError pattern. Two fast clicks of the extension's "Add as source" no longer 500 on the second one. - **Copy button on plain HTTP** — `navigator.clipboard` is gated by Secure Context (HTTPS-only). New `copyText()` helper falls back to `document.execCommand('copy')` via an off-screen textarea. Applied to ErrorDetailModal, ExtensionKeyBar, BrowserExtensionCard. ### Migrations - `0021_image_provenance_unique` — dedupe + `UNIQUE(image_record_id, post_id)`. - `0022_source_per_artist_platform` — collapse per-post Sources, reparent Posts + ImageProvenance, merge Post collisions. ## Test plan - [ ] Deploy `:latest`, force-update `app`, `worker`, `ml-worker`, `scheduler`, `web`. Alembic auto-applies 0021 + 0022. - [ ] Verify Atole's artist page now shows 1 Source under Management → Subscriptions. - [ ] Verify the new artist view: Posts tab default when posts exist, Gallery fallback when zero, frosted header continuous with TopNav. - [ ] Settings → Maintenance → Browser extension: install button still works in Firefox. - [ ] Settings → Maintenance: new ThumbnailBackfillCard present in the grid. - [ ] System Activity → click an error: modal shows task context + crisp pre-block; Copy button works on plain HTTP. - [ ] Walk through the FC-3g verification runbook (extension install → cookies push → quick-add-source). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledCurator#23