Add the Rotation check: MarkTried and keep/drop decisions
CI / ci (push) Successful in 3m28s
CI / ci (pull_request) Successful in 3m7s

Per D-021, plan.MarkTried(date) moves every Quest log dinner planned on
a household-local date (any plan status) to tried and opens a pending
rotation_reviews row; a dinner already checked for the date is left as
it is, so it is idempotent and never re-marks a dinner returned to the
Quest log.

GET /api/v1/rotation-reviews lists the pending checks as
{dinner {id, name, photo}, plan_date}. POST
/api/v1/dinners/{id}/rotation-review {decision} applies keep (Regular,
origin kept), drop ("Not for us", archived) or not_made ("We didn't
make it", back to the Quest log), closing the check with decided_at and
the session's updated_by; a second decision is 409 no_pending_review.

A dropped dinner's name joins the passed names: ListDeclinedSuggestionNames
and ListRecentDeclinedNames become ListPassedNames and
ListRecentPassedNames, which also read Rotation checks decided drop, so
the Side quest dedupe and household context never suggest it again.

A static test guards that keep is the only path to Regular: every query
writing dinners.status and every use of the regular status is reviewed.

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:14:01 -04:00
co-authored by Claude Opus 5.5
parent 2ed5f69783
commit f97b4c7b8c
19 changed files with 1234 additions and 54 deletions
+5 -5
View File
@@ -15,8 +15,8 @@ import (
"code.cubecraftlabs.com/Maniacs_Mansion/Meal-Quest/internal/store"
)
// PassedLimit is how many of the most recently passed Side quests the
// household context holds.
// PassedLimit is how many of the most recently passed names (Side quests
// passed and dinners a Rotation check dropped) the household context holds.
const PassedLimit = 60
// PlanWeeks is how many ISO weeks of plans, ending with the current week,
@@ -34,8 +34,8 @@ type HouseholdContext struct {
Regulars []RegularDinner
// QuestLog holds the Quest log dinner names, in name order.
QuestLog []string
// Passed holds the names of the most recently passed Side quests,
// oldest first.
// Passed holds the most recently passed names, oldest first: Side
// quests passed and dinners a Rotation check dropped ("Not for us").
Passed []string
// Plans are the planned dinners of the last PlanWeeks weeks, oldest
// first.
@@ -137,7 +137,7 @@ func readContext(ctx context.Context, st *store.Store, now time.Time) (h Househo
h.QuestLog = append(h.QuestLog, d.Name)
}
passed, err := q.ListRecentDeclinedNames(ctx, PassedLimit)
passed, err := q.ListRecentPassedNames(ctx, PassedLimit)
if err != nil {
return h, fmt.Errorf("read passed Side quests: %w", err)
}