Three UX-impacting fixes from 2026-05-30 afternoon. All "efficiency that hurt UX" reversals per the operator's standing principle.
adeee64 + 1eefed9 — Showcase + gallery initial loads now batch into 5-item chunks (12×5 for showcase, 10×5 for gallery) instead of one big 60/50 request. Total preserved. Items render progressively as each batch lands; combined with MasonryGrid's animateFromIndex, the showcase cascades batches in as they arrive instead of one big pop-in.
44bb12a — Thumbnail URL derived from stored thumbnail_path instead of re-deriving from (sha256, mime). The MIME-based extension predicate disagreed with the thumbnailer's transparency-based predicate (.png if alpha else .jpg) → every PNG-without-alpha and every WebP-with-alpha 404'd despite the file existing. Backfill was never the bug — it checks the correct on-disk path; the URL construction was the broken side. All 8 callers updated. No DB migration or backfill rerun needed.
810baf6 — Archive probe (safe_probe.probe_archive) switched from multiprocessing.Process to subprocess.run. The MP version crashed every import_archive_file in 49ms with AssertionError: daemonic processes are not allowed to have children (Celery prefork pool runs tasks in daemon processes; Python's multiprocessing forbids daemons from spawning children). Same crash isolation via subprocess; no daemon restriction. Layer-3 isolation added 2026-05-28 in [poison-pill resilience] was effectively a hard-blocker on the import path it was meant to protect.
No alembic migrations. New backend surface: backend/app/utils/_archive_probe_runner.py (subprocess entrypoint).
Test plan
After :latest rebuilds and compose restart:
Batched loads: Showcase + gallery initial fetches appear as ~5-item progressive cascades, not one big pop-in. Network panel shows multiple /api/showcase?limit=5 calls instead of one limit=60.
Thumbnail URL fix: Previously-broken thumbnails (gray placeholders in the operator's screenshot — image 44892, 44963, 44882, 44948, 48137, etc.) now render correctly. DevTools confirms the <img>src is the stored thumbnail_path and the file 200s.
Archive imports: Trigger an archive import (cbz / zip / rar / 7z). Completes end-to-end with media members extracted; no immediate AssertionError in 49ms; the import_archive_file task either succeeds or fails for a legitimate reason (bomb-guard, integrity, member-level).
## Summary
Three UX-impacting fixes from 2026-05-30 afternoon. All "efficiency that hurt UX" reversals per the operator's standing principle.
- **`adeee64` + `1eefed9`** — Showcase + gallery initial loads now batch into 5-item chunks (12×5 for showcase, 10×5 for gallery) instead of one big 60/50 request. Total preserved. Items render progressively as each batch lands; combined with MasonryGrid's `animateFromIndex`, the showcase cascades batches in as they arrive instead of one big pop-in.
- **`44bb12a`** — Thumbnail URL derived from stored `thumbnail_path` instead of re-deriving from `(sha256, mime)`. The MIME-based extension predicate disagreed with the thumbnailer's transparency-based predicate (`.png` if `alpha` else `.jpg`) → every PNG-without-alpha and every WebP-with-alpha 404'd despite the file existing. Backfill was never the bug — it checks the correct on-disk path; the URL construction was the broken side. All 8 callers updated. **No DB migration or backfill rerun needed.**
- **`810baf6`** — Archive probe (`safe_probe.probe_archive`) switched from `multiprocessing.Process` to `subprocess.run`. The MP version crashed every `import_archive_file` in 49ms with `AssertionError: daemonic processes are not allowed to have children` (Celery prefork pool runs tasks in daemon processes; Python's multiprocessing forbids daemons from spawning children). Same crash isolation via subprocess; no daemon restriction. Layer-3 isolation added 2026-05-28 in [poison-pill resilience] was effectively a hard-blocker on the import path it was meant to protect.
No alembic migrations. New backend surface: `backend/app/utils/_archive_probe_runner.py` (subprocess entrypoint).
## Test plan
After `:latest` rebuilds and compose restart:
- [ ] **Batched loads:** Showcase + gallery initial fetches appear as ~5-item progressive cascades, not one big pop-in. Network panel shows multiple `/api/showcase?limit=5` calls instead of one `limit=60`.
- [ ] **Thumbnail URL fix:** Previously-broken thumbnails (gray placeholders in the operator's screenshot — image 44892, 44963, 44882, 44948, 48137, etc.) now render correctly. DevTools confirms the `<img>` `src` is the stored `thumbnail_path` and the file 200s.
- [ ] **Archive imports:** Trigger an archive import (cbz / zip / rar / 7z). Completes end-to-end with media members extracted; no immediate `AssertionError` in 49ms; the `import_archive_file` task either succeeds or fails for a legitimate reason (bomb-guard, integrity, member-level).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Three UX-impacting fixes from 2026-05-30 afternoon. All "efficiency that hurt UX" reversals per the operator's standing principle.
adeee64+1eefed9— Showcase + gallery initial loads now batch into 5-item chunks (12×5 for showcase, 10×5 for gallery) instead of one big 60/50 request. Total preserved. Items render progressively as each batch lands; combined with MasonryGrid'sanimateFromIndex, the showcase cascades batches in as they arrive instead of one big pop-in.44bb12a— Thumbnail URL derived from storedthumbnail_pathinstead of re-deriving from(sha256, mime). The MIME-based extension predicate disagreed with the thumbnailer's transparency-based predicate (.pngifalphaelse.jpg) → every PNG-without-alpha and every WebP-with-alpha 404'd despite the file existing. Backfill was never the bug — it checks the correct on-disk path; the URL construction was the broken side. All 8 callers updated. No DB migration or backfill rerun needed.810baf6— Archive probe (safe_probe.probe_archive) switched frommultiprocessing.Processtosubprocess.run. The MP version crashed everyimport_archive_filein 49ms withAssertionError: daemonic processes are not allowed to have children(Celery prefork pool runs tasks in daemon processes; Python's multiprocessing forbids daemons from spawning children). Same crash isolation via subprocess; no daemon restriction. Layer-3 isolation added 2026-05-28 in [poison-pill resilience] was effectively a hard-blocker on the import path it was meant to protect.No alembic migrations. New backend surface:
backend/app/utils/_archive_probe_runner.py(subprocess entrypoint).Test plan
After
:latestrebuilds and compose restart:/api/showcase?limit=5calls instead of onelimit=60.<img>srcis the storedthumbnail_pathand the file 200s.AssertionErrorin 49ms; theimport_archive_filetask either succeeds or fails for a legitimate reason (bomb-guard, integrity, member-level).🤖 Generated with Claude Code