Keep a dropped dinner's name immutable and 404 a never-checked dinner
CI / ci (push) Successful in 3m37s
CI / ci (pull_request) Successful in 3m14s

R-01: a Rotation check decided drop now records the dinner's name at the
decision in rotation_reviews.name (migration 0014, a table rebuild as 0012
did for suggestions; checks dropped before it take their dinner's name).
ListPassedNames and ListRecentPassedNames read that name instead of the
mutable dinners.name, so renaming the archived dinner no longer lets the
dropped dish back into a Side quest batch. A CHECK keeps a name on drops
only.

R-02: deciding on a dinner that has never had a Rotation check is now
ErrReviewNotFound, 404 not_found; a dinner whose checks are all decided,
such as a second decision, stays 409 no_pending_review.

Tests: TestLaterBatchNeverResuggestsRenamedDrop (drop, rename, next batch
still refuses the original name), TestMigrateRotationReviewName (0013 to
0014 upgrade and constraints), TestPostRotationReviewNeverChecked (404),
and TestDecideReview/TestDecideReviewRefused updated.

Maestro: a1887d1c-c405-478c-ac7d-8cb8b40d280d

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 07:32:05 -04:00
co-authored by Claude Opus 5.5
parent f97b4c7b8c
commit cca6a82810
16 changed files with 320 additions and 58 deletions
+2 -3
View File
@@ -8,10 +8,9 @@ SELECT name FROM (
FROM suggestions
WHERE status = 'declined'
UNION ALL
SELECT dinners.name, rotation_reviews.decided_at, rotation_reviews.id
SELECT name, decided_at, id
FROM rotation_reviews
JOIN dinners ON dinners.id = rotation_reviews.dinner_id
WHERE rotation_reviews.decision = 'drop'
WHERE decision = 'drop'
)
ORDER BY decided_at DESC, id DESC
LIMIT ?;
+8 -2
View File
@@ -40,12 +40,18 @@ ORDER BY rotation_reviews.plan_date, rotation_reviews.id;
SELECT * FROM rotation_reviews
WHERE dinner_id = ? AND status = 'pending';
-- name: CountRotationReviews :one
-- How many Rotation checks dinner_id has had, pending or decided.
SELECT COUNT(*) FROM rotation_reviews WHERE dinner_id = ?;
-- name: DecideRotationReview :execrows
-- Records the household's decision on a pending Rotation check; one
-- already decided is left as it is (0 rows).
-- Records the household's decision on a pending Rotation check, with the
-- dinner's name for a drop and NULL otherwise; one already decided is left
-- as it is (0 rows).
UPDATE rotation_reviews
SET status = 'decided',
decision = sqlc.arg(decision),
name = sqlc.narg(name),
decided_at = sqlc.arg(now),
updated_by = sqlc.arg(updated_by),
updated_at = sqlc.arg(now)
+4 -5
View File
@@ -89,17 +89,16 @@ WHERE suggestions.status = 'pending' AND dinners.status = 'suggested';
-- name: ListPassedNames :many
-- Every name the household turned down, oldest decision first: the name
-- each declined suggestion was suggested under, which renaming its dinner
-- does not change, and the name of each dinner a Rotation check dropped
-- ("Not for us"), which archiving keeps.
-- does not change, and the name each dinner a Rotation check dropped
-- ("Not for us") had when dropped, which renaming it does not change.
SELECT name FROM (
SELECT name, decided_at, id
FROM suggestions
WHERE status = 'declined'
UNION ALL
SELECT dinners.name, rotation_reviews.decided_at, rotation_reviews.id
SELECT name, decided_at, id
FROM rotation_reviews
JOIN dinners ON dinners.id = rotation_reviews.dinner_id
WHERE rotation_reviews.decision = 'drop'
WHERE decision = 'drop'
)
ORDER BY decided_at, id;