Thumbnails #2

Closed
opened 2026-05-17 12:57:47 +00:00 by erik · 5 comments
Owner

Thumbnails should be constrained to a width of 256 pixels, letting the height be derived from the optimized photo's dimensions so as not to distort the thumbnail.

Make sure there is a dedicated API endpoint to [re-]generate a thumbnail for a photo. This will be useful in the future (eg backfill thumbnails after a major change).

Make sure that thumbnail processing has its own Task definition.

Create ./wiki/thumbnails.md to record any important details about how we process thumbnails.

Thumbnails should be constrained to a width of 256 pixels, letting the height be derived from the optimized photo's dimensions so as not to distort the thumbnail. Make sure there is a dedicated API endpoint to [re-]generate a thumbnail for a photo. This will be useful in the future (eg backfill thumbnails after a major change). Make sure that thumbnail processing has its own Task definition. Create ./wiki/thumbnails.md to record any important details about how we process thumbnails.
erik changed title from Thumbnail dimensions to Thumbnails 2026-05-17 13:08:47 +00:00
agent-pm removed their assignment 2026-05-18 22:24:13 +00:00
agent-pm removed their assignment 2026-05-23 03:06:35 +00:00
Member

Brief — Developer

Phase: simple. Run fjx dev prompt simple for the cycle protocol.

<!-- pm:brief:dev --> # Brief — Developer **Phase:** simple. Run `fjx dev prompt simple` for the cycle protocol.
Member

Ledger for: agent-pm

Status: closed @ 2026-05-23T04:42:16.070Z
Briefs:

Activity

  1. i2/closed : applied

(PM manages this ledger; agent section unused)

<!-- agent-pm:ledger --> ## Ledger for: agent-pm Status: closed @ 2026-05-23T04:42:16.070Z Briefs: - [dev](#issuecomment-1790) - [qa](#issuecomment-1856) - build: absent ### Activity 1. i2/closed : applied --- _(PM manages this ledger; agent section unused)_
Member

Ledger for: agent-dev

Status: review @ 2026-05-23T03:59:38.620Z


Ledger for: agent-dev

Tasks

  • Cycle 1: split thumbnail derivation into a dedicated generate-thumbnail task (256px proportional, sourced from url_optimized), add POST /api/photos/:id/regenerate-thumbnail, write wiki/thumbnails.md. PR #20. just validate green. Commit e65312c.
  • Cycle 2 (QA concerns): add test coverage for the new handler and endpoint. just validate green. Commit e3a0bdb (push to feat/thumbnails).
    • src/lib/pois/tasks.test.ts — 5 tests: 256px proportional output (800×400 → 256×128), withoutEnlargement (100×50 stays 100×50), legacy-path cleanup (.jpg.avif), no-op when url_optimized is missing, and optimize-photo → generate-thumbnail enqueue cascade.
    • src/routes/api/photos/curation-endpoints.test.ts — adds regenerate-thumbnail to the per-verb owner-only / 403-stranger / single-enqueue table (asserts the endpoint enqueues exactly generate-thumbnail).
  • Wiki cosmetic fix (QA gap #5): "writes width/height on the photos row" — columns live on photos, not pois.

Notes for QA

  • QA gap #2 (brittle basePath derivation): left as-is. Both handlers operate on canonical paths produced by uploadPhoto (<base>/original.<ext> and <base>/optimized.avif), so the regex no-op case requires hand-edited storage paths or a future refactor. Filing a follow-up issue would be in scope for a hardening pass, not this change.
  • QA gap #4 (restore-optimized header doc drift): the comment is still accurate post-cascade — the endpoint does re-derive both variants via optimize-photo enqueueing generate-thumbnail. Left untouched.
  • Local infra note from cycle 1 still applies: just test::env writes wrong host ports on supabase CLI 2.98.2; patched .env.test manually each cycle.

Pull request

  • PR #20: #20
  • Latest commit: e3a0bdb test(thumbnails): cover generate-thumbnail handler and regen endpoint
<!-- agent-dev:ledger --> ## Ledger for: agent-dev Status: review @ 2026-05-23T03:59:38.620Z --- ## Ledger for: agent-dev ### Tasks - [x] Cycle 1: split thumbnail derivation into a dedicated `generate-thumbnail` task (256px proportional, sourced from `url_optimized`), add `POST /api/photos/:id/regenerate-thumbnail`, write `wiki/thumbnails.md`. PR #20. `just validate` green. Commit `e65312c`. - [x] Cycle 2 (QA concerns): add test coverage for the new handler and endpoint. `just validate` green. Commit `e3a0bdb` (push to `feat/thumbnails`). - `src/lib/pois/tasks.test.ts` — 5 tests: 256px proportional output (800×400 → 256×128), `withoutEnlargement` (100×50 stays 100×50), legacy-path cleanup (`.jpg` → `.avif`), no-op when `url_optimized` is missing, and `optimize-photo → generate-thumbnail` enqueue cascade. - `src/routes/api/photos/curation-endpoints.test.ts` — adds `regenerate-thumbnail` to the per-verb owner-only / 403-stranger / single-enqueue table (asserts the endpoint enqueues exactly `generate-thumbnail`). - [x] Wiki cosmetic fix (QA gap #5): "writes width/height on the photos row" — columns live on `photos`, not `pois`. ### Notes for QA - QA gap #2 (brittle `basePath` derivation): left as-is. Both handlers operate on canonical paths produced by `uploadPhoto` (`<base>/original.<ext>` and `<base>/optimized.avif`), so the regex no-op case requires hand-edited storage paths or a future refactor. Filing a follow-up issue would be in scope for a hardening pass, not this change. - QA gap #4 (`restore-optimized` header doc drift): the comment is still accurate post-cascade — the endpoint does re-derive both variants via `optimize-photo` enqueueing `generate-thumbnail`. Left untouched. - Local infra note from cycle 1 still applies: `just test::env` writes wrong host ports on supabase CLI 2.98.2; patched `.env.test` manually each cycle. ### Pull request - PR #20: https://git.tfks.net/tfks/trippin/pulls/20 - Latest commit: `e3a0bdb test(thumbnails): cover generate-thumbnail handler and regen endpoint`
agent-dev removed their assignment 2026-05-23 03:17:28 +00:00
agent-pm removed their assignment 2026-05-23 03:20:07 +00:00
Member

Brief — QA

Phase: review. Run fjx qa prompt default for the cycle protocol.

<!-- pm:brief:qa --> # Brief — QA **Phase:** review. Run `fjx qa prompt default` for the cycle protocol.
Member

Ledger for: agent-qa

Status: concerns @ 2026-05-23T03:34:33.564Z


Ledger for: agent-qa

Action summary

  • validate.yaml run 40 — success (html_url). Covers fmt, lint, type-check, full test suite + coverage, audit, secrets, SAST, fuzz on commit e65312c.
  • Coverage delta: global lines 75.1 → 75.3, branches 73.2 → 74.1. linesFound grew 2410 → 2524 (+114) — new code is being measured. Net +91 lines hit.

Diff scope

  • src/lib/pois/tasks.ts — splits optimize-photo into two handlers (optimize-photo + generate-thumbnail). THUMBNAIL_WIDTH = 256, withoutEnlargement: true, no crop, sourced from url_optimized.
  • src/routes/api/photos/[id]/regenerate-thumbnail.ts — owner-only POST, enqueues generate-thumbnail, returns 202 + task_id. Mirrors restore-optimized.ts exactly in shape.
  • wiki/thumbnails.md — policy + pipeline doc.
  • src/main.ts already imports ./lib/pois/tasks.ts, so the new generate-thumbnail handler registers on startup. No wiring gap.

Gap analysis (what the actions did not catch)

  1. New code is uncovered by tests. src/lib/pois/tasks.ts and src/routes/api/photos/[id]/regenerate-thumbnail.ts are absent from coverage/summary.json's files map (sibling restore-optimized.ts is covered). The validate green comes from existing curation/upload tests that touch pois/tasks.ts only indirectly — none assert the new generate-thumbnail behavior (256-cap, proportional height, no enlargement, sourced from optimized, path replace, legacy delete) or the new endpoint (owner-only 404/403/202, task id returned).
  2. basePath derivation is brittle. Both handlers compute basePath via replace(/\/(original|optimized)\.[^/]+$/, ""). If url_optimized ever lacks the exact /optimized.<ext> suffix (path migration, manual edit, future refactor), the replace is a no-op and thumbnailPath becomes <full_url>/thumbnail.avif — a silently nested path. Not exploitable today, but a class of bug a test would catch.
  3. No assertion that generate-thumbnail short-circuits safely when url_optimized is missing. A regen request fired before optimize completes returns 202 and the task silently no-ops; nothing tests that path.
  4. Doc drift in restore-optimized.ts header. Says "re-derive default optimized + thumbnail" — still true via the new cascade, but no longer literal. Cosmetic.
  5. Wiki nit: "writes width/height on the pois row" — columns are on photos, not pois. Cosmetic.

Confirmed issues

None blocking. All five items above are concerns, not regressions: the actions cover what they were aimed at, and the code matches the issue requirements (256px proportional, dedicated task, dedicated endpoint, wiki doc).

Untested assumptions

  • Cascading optimize-photogenerate-thumbnail enqueue runs in the same transaction as the photos UPDATE (relies on processTaskBatch wrapping the handler in a tx — it does, per lib/worker/tasks.ts), so partial-failure orphans should be impossible. Not directly tested for this pair.
  • withoutEnlargement: true for photos narrower than 256px is sharp's documented behavior; not exercised by any fixture in this diff.
  • Concurrent re-runs of generate-thumbnail for the same photo are idempotent (same path, same UPDATE value); not stressed.

Verdict

concerns — implementation matches the issue. Routing back to dev to add minimal coverage for generate-thumbnail and the new endpoint (mirror the restore-optimized test pattern). Items 2–5 are optional follow-ups.

<!-- agent-qa:ledger --> ## Ledger for: agent-qa Status: concerns @ 2026-05-23T03:34:33.564Z --- ## Ledger for: agent-qa ### Action summary - `validate.yaml` run 40 — **success** ([html_url](https://git.tfks.net/tfks/trippin/actions/runs/40)). Covers fmt, lint, type-check, full test suite + coverage, audit, secrets, SAST, fuzz on commit `e65312c`. - Coverage delta: global lines **75.1 → 75.3**, branches **73.2 → 74.1**. `linesFound` grew 2410 → 2524 (+114) — new code is being measured. Net +91 lines hit. ### Diff scope - `src/lib/pois/tasks.ts` — splits `optimize-photo` into two handlers (`optimize-photo` + `generate-thumbnail`). `THUMBNAIL_WIDTH = 256`, `withoutEnlargement: true`, no crop, sourced from `url_optimized`. - `src/routes/api/photos/[id]/regenerate-thumbnail.ts` — owner-only POST, enqueues `generate-thumbnail`, returns 202 + `task_id`. Mirrors `restore-optimized.ts` exactly in shape. - `wiki/thumbnails.md` — policy + pipeline doc. - `src/main.ts` already imports `./lib/pois/tasks.ts`, so the new `generate-thumbnail` handler registers on startup. No wiring gap. ### Gap analysis (what the actions did not catch) 1. **New code is uncovered by tests.** `src/lib/pois/tasks.ts` and `src/routes/api/photos/[id]/regenerate-thumbnail.ts` are absent from `coverage/summary.json`'s `files` map (sibling `restore-optimized.ts` *is* covered). The validate green comes from existing curation/upload tests that touch `pois/tasks.ts` only indirectly — none assert the new generate-thumbnail behavior (256-cap, proportional height, no enlargement, sourced from optimized, path replace, legacy delete) or the new endpoint (owner-only 404/403/202, task id returned). 2. **`basePath` derivation is brittle.** Both handlers compute `basePath` via `replace(/\/(original|optimized)\.[^/]+$/, "")`. If `url_optimized` ever lacks the exact `/optimized.<ext>` suffix (path migration, manual edit, future refactor), the replace is a no-op and `thumbnailPath` becomes `<full_url>/thumbnail.avif` — a silently nested path. Not exploitable today, but a class of bug a test would catch. 3. **No assertion that `generate-thumbnail` short-circuits safely when `url_optimized` is missing.** A regen request fired before optimize completes returns 202 and the task silently no-ops; nothing tests that path. 4. **Doc drift in `restore-optimized.ts` header.** Says "re-derive default optimized + thumbnail" — still true via the new cascade, but no longer literal. Cosmetic. 5. **Wiki nit:** "writes width/height on the pois row" — columns are on `photos`, not `pois`. Cosmetic. ### Confirmed issues None blocking. All five items above are concerns, not regressions: the actions cover what they were aimed at, and the code matches the issue requirements (256px proportional, dedicated task, dedicated endpoint, wiki doc). ### Untested assumptions - Cascading `optimize-photo` → `generate-thumbnail` enqueue runs in the same transaction as the photos UPDATE (relies on `processTaskBatch` wrapping the handler in a tx — it does, per `lib/worker/tasks.ts`), so partial-failure orphans should be impossible. Not directly tested for this pair. - `withoutEnlargement: true` for photos narrower than 256px is sharp's documented behavior; not exercised by any fixture in this diff. - Concurrent re-runs of `generate-thumbnail` for the same photo are idempotent (same path, same UPDATE value); not stressed. ### Verdict **concerns** — implementation matches the issue. Routing back to dev to add minimal coverage for `generate-thumbnail` and the new endpoint (mirror the `restore-optimized` test pattern). Items 2–5 are optional follow-ups.
agent-qa removed their assignment 2026-05-23 03:35:07 +00:00
agent-dev removed their assignment 2026-05-23 03:59:45 +00:00
erik reopened this issue 2026-05-23 04:16:05 +00:00
Sign in to join this conversation.
No description provided.