feat: supplements move to the daily plan (per-day, carry-forward) - #181
Merged
Conversation
Supplements were three flags on the nutrition profile — a standing setting, so a nutrient read "covered" every day whether or not you actually took the pill. Whether you took your B12 today is a fact about today, so supplements now live on the day's plan. - New PRIVATE day_supplements table (one row per user+date). The effective supplements for a date is that date's row, else the most recent earlier row (carry-forward), else none — so a new day inherits your last routine without re-checking boxes, and an explicit edit pins that day forward. - targets() now reads the day's effective supplements instead of the profile, so supplement coverage is correctly per-day. The Day screen gains a supplements checklist; the profile form drops its dead one. - The records ride the existing authed diary pull/apply and a new saveDaySupplements outbox op, so desktop stays local-first + synced; web posts to POST /api/day-supplements. - Migration: a boot-time seed copies any legacy profile supplement flags into a single carry-forward floor row (1970-01-01), so existing coverage is preserved with no visible change. Guarded on the old columns existing and idempotent; the dead profiles.supplement_* columns are left in place (SQLite can't cheaply drop them) and no longer read. Verified: full `just check` green; new core carry-forward test + server migration tests; live boot against a prod-shaped DB seeds the floor row, and an authed round-trip confirms per-day coverage (explicit row wins, earlier days inherit the floor, later days carry forward).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Product feedback (7/23): supplements belong in the daily plan, not the nutrition profile.
They were three flags on
profiles— a standing setting, so a nutrient read "covered" every day regardless of whether the pill was actually taken. Whether you took your B12 today is a fact about the day.Model
day_supplementstable, one row per (user, date). The effective supplements for a date = that date's row, else the most recent earlier row (carry-forward), else none. A new day inherits your last routine (no re-checking boxes every morning); an explicit toggle pins that day and carries forward from there.vegify-core::targets()now takes the day's effective supplements instead of the profile, sosupplementCoveredis correctly per-day. The Day screen gains a supplements checklist (owner-only); the profile form's dead checklist is removed.saveDaySupplementsoutbox op (desktop stays local-first + synced); web posts toPOST /api/day-supplements.Migration (preserves existing coverage)
A boot-time, pragma-guarded, idempotent seed copies any legacy
profiles.supplement_*flags into a single carry-forward floor row dated1970-01-01, so carry-forward covers every real day and nothing visibly changes. The deadprofiles.supplement_*columns are left in place (SQLite can't cheaply drop a column) and are no longer read; the Drizzle schema, desktopschema.sql, and serverensure_schemadrop them so fresh DBs never grow them.A design note (data model, seeding, coverage, migration) was written before implementation.
Verification
just checkgreen — lint, typecheck, web tests, web + desktop builds, all Rust tests, schema-parity + bindings drift.vegify-corecarry-forward + pull-round-trip test; new server migration tests (floor-seed from old flags, idempotent, fresh-DB skip, effective coverage).1970-01-01|1|0|1; POST no-bearer → 401 (route live + gated). An authed round-trip confirms: an explicit row wins for its day, earlier days inherit the floor, later days carry the explicit row forward.