Photo cache and /media endpoint #68

Member

Maestro task: 8a432108-d3ba-4013-ae8c-11963981d641 (Feature 10 - Recipe Grounding and Photos).

Done-condition

recipes.CachePhoto(url) downloads via the safe Fetcher (image content types only, 8 MB image cap), decodes JPEG/PNG/WebP/GIF (first frame), rejects images <300 px on the short side, scales to 1200 px and 400 px on the long side preserving aspect ratio (golang.org/x/image/draw), encodes JPEG quality 82, stores under $MEALQUEST_DATA_DIR/media/photos/{id}-{size}.jpg, dedupes by SHA-256 of original bytes and records a photos row. GET /media/photos/{id}-{400|1200}.jpg (session required) serves with Cache-Control "private, max-age=31536000, immutable"; unknown -> 404. Tests cover each format, too-small rejection, dedupe, sizes and endpoint headers. Go quality floor passes.

Evidence

  • New photo cache + /media endpoint; migration/sqlc as needed; golang.org/x/image (approved) added for draw.
  • gofmt/vet/go test -race/build pass; sqlc-check no drift.

Note: the developer observed the existing test TestFetchCanceledWaitHoldsNoSlot (from task f4414e41) failing with EOF under heavy CPU load; it also fails on base 2fb7807, so it is a pre-existing flaky test, not caused here.

Maestro task: 8a432108-d3ba-4013-ae8c-11963981d641 (Feature 10 - Recipe Grounding and Photos). ## Done-condition recipes.CachePhoto(url) downloads via the safe Fetcher (image content types only, 8 MB image cap), decodes JPEG/PNG/WebP/GIF (first frame), rejects images <300 px on the short side, scales to 1200 px and 400 px on the long side preserving aspect ratio (golang.org/x/image/draw), encodes JPEG quality 82, stores under $MEALQUEST_DATA_DIR/media/photos/{id}-{size}.jpg, dedupes by SHA-256 of original bytes and records a photos row. GET /media/photos/{id}-{400|1200}.jpg (session required) serves with Cache-Control "private, max-age=31536000, immutable"; unknown -> 404. Tests cover each format, too-small rejection, dedupe, sizes and endpoint headers. Go quality floor passes. ## Evidence - New photo cache + /media endpoint; migration/sqlc as needed; golang.org/x/image (approved) added for draw. - gofmt/vet/go test -race/build pass; sqlc-check no drift. Note: the developer observed the existing test TestFetchCanceledWaitHoldsNoSlot (from task f4414e41) failing with EOF under heavy CPU load; it also fails on base 2fb7807, so it is a pre-existing flaky test, not caused here.
dev_agent added 1 commit 2026-10-05 14:03:05 -04:00
Add photo cache and /media/photos endpoint
CI / ci (push) Successful in 1m38s
CI / ci (pull_request) Successful in 1m39s
0347e11b7d
recipes.PhotoCache.CachePhoto downloads an image through the safe
Fetcher's new FetchImage (JPEG/PNG/WebP/GIF content types only, 8 MB
cap, uncached), decodes it (GIF first frame), refuses images under
300 px on the short side, scales to 1200 px and 400 px on the long side
with golang.org/x/image/draw, encodes JPEG quality 82 under
$MEALQUEST_DATA_DIR/media/photos/{id}-{size}.jpg, deduplicates by the
SHA-256 of the original bytes and records a photos row.

GET /media/photos/{id}-{400|1200}.jpg serves a stored size behind the
session with Cache-Control "private, max-age=31536000, immutable";
anything unknown is 404.

Adds golang.org/x/image v0.45.0 (approved; v0.46.0 needs Go 1.26).

Maestro task: 8a432108-d3ba-4013-ae8c-11963981d641

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
overseer added 1 commit 2026-10-05 14:03:06 -04:00
Add photo cache and /media/photos endpoint
CI / ci (push) Successful in 1m38s
CI / ci (pull_request) Successful in 1m39s
0347e11b7d
recipes.PhotoCache.CachePhoto downloads an image through the safe
Fetcher's new FetchImage (JPEG/PNG/WebP/GIF content types only, 8 MB
cap, uncached), decodes it (GIF first frame), refuses images under
300 px on the short side, scales to 1200 px and 400 px on the long side
with golang.org/x/image/draw, encodes JPEG quality 82 under
$MEALQUEST_DATA_DIR/media/photos/{id}-{size}.jpg, deduplicates by the
SHA-256 of the original bytes and records a photos row.

GET /media/photos/{id}-{400|1200}.jpg serves a stored size behind the
session with Cache-Control "private, max-age=31536000, immutable";
anything unknown is 404.

Adds golang.org/x/image v0.45.0 (approved; v0.46.0 needs Go 1.26).

Maestro task: 8a432108-d3ba-4013-ae8c-11963981d641

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent requested changes 2026-10-05 14:05:57 -04:00
Dismissed
review_agent left a comment
Member

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
overseer added 1 commit 2026-10-05 14:14:48 -04:00
Validate decoded photo size and require a photos row to serve
CI / ci (push) Successful in 1m37s
CI / ci (pull_request) Successful in 1m37s
70f545152a
decodePhoto checked the GIF logical screen size, but gif.Decode returns
the first frame, which may be smaller; a 400x400 screen with a 1x1 first
frame passed and was scaled. The short-side check now measures the
decoded image's bounds. The pixel cap still comes from the header, before
decoding.

OpenPhoto served any file whose name was a canonical ULID and stored
size, with no photos row required. It now looks the id up with the new
GetPhoto query and reports an id with no row as fs.ErrNotExist, so
GET /media/photos/{id}-{size}.jpg is 404 for it even when a file exists.
OpenPhoto takes the request context for the lookup.

The media test fixture records a photos row for its served photo and
adds an orphan photo whose files exist without a row; both its sizes
must be 404.

Maestro task: 8a432108-d3ba-4013-ae8c-11963981d641

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent left a comment
Member

Independent Codex gpt-6-sol review: APPROVED for this exact commit.

Independent Codex gpt-6-sol review: APPROVED for this exact commit.
approver_agent merged commit 58e8f30f0e into feature/cebed295-8c05-453b-807b-8078dcd8d250-recipe-grounding 2026-10-05 14:24:12 -04:00
Sign in to join this conversation.
No Reviewers
No labels
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Maniacs_Mansion/MealQuest#68