Neutral suggestions (scoreDelta = 0) are not conflicts — they simply
don't improve variety. Changing scoreDelta <= 0 to scoreDelta < 0
lets empty-plan additions and quality-neutral swaps show without a
misleading ⚠ Variationskonflikt warning.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
simulateVarietyScore was adding the candidate recipe on top of the
existing slot for slotDate, keeping the old recipe's tag-repeat penalty
in the score. Now the existing slot is excluded before simulating, so
swapping a recipe for one with better variety correctly shows positive
scoreDelta and hasConflict=false.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Eliminates duplicated currentSlots→score pattern that appeared in both
getSuggestions and getVarietyPreview.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces magic literal 10.0 with a named constant in all four
scoring sites: getSuggestions, getVarietyPreview, scoreFromSimulatedSlots,
and getVarietyScore.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SuggestionItem now exposes scoreDelta (simulatedScore − currentScore) and
hasConflict (scoreDelta ≤ 0) so the frontend can render badges without
needing to pass currentVarietyScore as a separate prop.
PlanningService.getSuggestions() computes currentScore once per request
and derives scoreDelta + hasConflict per candidate. Sorting is unchanged
(scoreDelta desc = simulatedScore desc since currentScore is constant).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add @RequiresHouseholdRole("member") to GET /{planId}/variety-preview endpoint
to require household membership (was accessible to any authenticated user)
- Extract scoreFromSimulatedSlots() private method eliminating duplicate logic
between simulateVarietyScore() and the old computeCurrentScore()
- Fix loose variety preview test assertions (isBetween → exact assertEquals)
- Add test verifying negative scoreDelta when candidate is a duplicate recipe
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Returns currentScore, projectedScore, and scoreDelta when a recipe
would be added on a given date. Used by C6 desktop day picker.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each domain had a single-implementation interface (e.g. AdminService
interface + AdminServiceImpl). Merged implementation into the service
class and deleted the redundant interfaces per KISS principle.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add VarietyScoreConfig entity, repository, and V020 migration for
per-household scoring weights and configurable tag types
- Rewrite getVarietyScore: tag-type repeats on consecutive days,
non-staple ingredient overlaps, cooking log history, plan duplicates
- Rewrite getSuggestions: simulate variety score for each candidate,
add tag filter (AND, case-insensitive) and configurable topN param
- Update SuggestionResponse to return simulatedScore instead of
fitReasons/warnings, update VarietyScoreResponse to new shape
- Seed default VarietyScoreConfig on household creation
- Extend test suite across all domains (+270 tests, all passing)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>