NutriMagnus v2026-08-31-2330
FIX: A NEWER RELEASE COULD STAY HIDDEN BEHIND ONE ALREADY SEEN THAT DAY
The daily/weekly/monthly notification-frequency setting was meant to throttle repeat notices about the same release, not hide a different, newer one that showed up later the same day — but that's what it was doing: once the banner had shown once today, a second release published an hour later wouldn't surface until tomorrow. It now always shows a release you haven't been told about yet, regardless of the frequency window; the frequency setting only limits repeat notices about a release you've already seen.
THE UPDATE NOW BUTTON NOW SHOWS A DOWNLOADING SPINNER
Clicking Update Now used to leave the button sitting there with no feedback while the new binary downloaded in the background — easy to mistake for nothing having happened, especially on a slower connection. It now disables itself and shows a spinner with "Downloading…" the moment you confirm, so it's clear the update is actually in progress.
Scope: web/backend.py (_should_show_update_notice() now takes the candidate
release's tag and only applies the frequency window when it matches the tag
last shown — a new prefs.json key, update_notice_last_shown_tag, tracks
this). web/templates/home.html (Update Now form's confirm handler moved out
of an inline onsubmit into a proper <script> block, disables the button and
swaps in a Bootstrap spinner on confirm). tests/test_web.py (new coverage
for the frequency-vs-newer-release interaction).
CHOOSE HOW OFTEN YOU'RE TOLD ABOUT NEW VERSIONS, AND ALWAYS SEE YOUR CURRENT ONE
Settings now has an "Update Notifications" section where you can set how often the "new version available" banner shows up on the home page: daily (the default), weekly, or monthly. The banner's build note also names that setting directly, with a link to change it. Separately, the home page now always shows a line under the Welcome heading — "Current version date: yyyy-mm-dd:hhmm" — so you can check exactly what you're running, down to the minute, without scrolling to the page footer; whenever a build note is set it follows in parentheses as "(Version note: ...)" on that same line. The build note no longer gets its own standalone box further up the page. See What you see on the home page for the full rundown, including exactly when the update check itself runs.
Scope: web/backend.py (_current_update_notify_frequency(), _should_show_update_notice()
gating index()'s update_available via a saved prefs.json frequency + last-shown-date pair;
new POST /settings/update-notify-frequency route; version_date passed as the full
VERSION stamp). web/templates/settings.html (new "Update Notifications" section).
web/templates/home.html (frequency note added next to NEW VERSION NOTE in the banner;
the old standalone "NEW VERSION NOTE" box removed; the always-visible "Current version
date" line below Welcome now carries the build note in parentheses). tests/test_web.py
(updated accordingly). user-manual.md (new "What you see on the home page" tour, Part 3
Section A). README-numa-documentation.md (matching, fuller technical writeup).
BUILD-NOTE LINE NOW LABELED "NEW VERSION NOTE:", AND ITS BROKEN RENAME FIXED
The plain-language note about your current build now reads "NEW VERSION NOTE: ..." instead of repeating the version number a second time on that line (the number's already in the line above it, or the page-bottom small print). Separately, version.py's note constant was renamed to NEW_VERSION_NOTE; the web app's own import of the old name was fixed to match (it briefly wouldn't start otherwise), and one of the two places that line is rendered had been missed in the wording update, leaving stale text visible in the UPDATE AVAILABLE banner specifically — that's fixed too.
Scope: version.py (VERSION_NOTE renamed to NEW_VERSION_NOTE), web/backend.py
(import and template-context key updated to match), web/templates/home.html
(both the update_available and standalone renderings of the note now read
"NEW VERSION NOTE: ..."). tests/test_web.py (updated to the new name and
an assertion added that was missing on the merged-into-banner case).
FIX: "UPDATE AVAILABLE" BANNER STILL SHOWED RIGHT AFTER A SUCCESSFUL UPDATE
After clicking Update Now, the just-updated confirmation and the "there's an update available" banner could both show at once — confusing, since one says you're done and the other says you're not. The running process doesn't reload its own version number until it's relaunched, so the availability check still (accurately, but unhelpfully) saw the old version and flagged the release you just installed as available. That check is now skipped for the one page load right after a successful update.
Scope: web/backend.py (index() skips the update_check call when the
updated query param is set). tests/test_web.py (1 new assertion).
FIX: "UPDATE NOW" SUCCESS MESSAGE TOLD YOU TO QUIT AN APP WITH NO VISIBLE WINDOW TO QUIT
After a successful in-place update, the message used to say "Quit and reopen NutriMagnus" — but the packaged install has no visible window or taskbar entry to quit from, only the browser tab. It now says "Close this browser tab, then relaunch NutriMagnus," which is both accurate (the background server process only picks up the new binary on relaunch, not just from closing the tab) and matches what a user actually sees on screen.
Scope: web/templates/home.html (UPDATED banner wording), user-manual.md
(matching wording in the Update Now description). tests/test_web.py (1
new assertion).
FIX: THE CURRENT-BUILD VERSION NOTE WAS BURIED IN FINE PRINT AT THE PAGE BOTTOM
The plain-language note describing what changed in your current build (version.py's VERSION_NOTE) used to appear only in small grey text at the very bottom of the home page — easy to miss entirely. It now shows near the top of the page: when there's an update available, it appears as a second line inside that same UPDATE AVAILABLE box, right below the first line; otherwise it gets its own light box in that same spot. The bare version number stays in the small print at the page bottom either way, for reference.
Scope: web/templates/home.html (version_note now renders as a line inside
the update_available alert box when one is shown — not a separate box
below it — falling back to its own alert-secondary box only when there's
no update available, so it's never shown twice; the bare version number
stays in the page-bottom small print, unchanged). tests/test_web.py (2
new tests covering both placements and no duplication).
NEW: ONE-CLICK "UPDATE NOW" BUTTON ON THE UPDATE-AVAILABLE BANNER
If you're running the packaged Linux install, the home page's UPDATE AVAILABLE banner now has an Update Now button — no terminal, no manual download. It fetches the latest release and replaces the running program in place; your data lives elsewhere and is never touched. A message tells you when it's safe to quit and reopen NuMa to start using the new version — the copy you're currently running keeps working until you do. Running from source instead of the packaged install shows the plain "what's new on GitHub" link as before, since there's no packaged binary for the button to replace.
Scope: numa_app/services/self_update.py (new — perform_update() downloads
the latest release's binary/icon from GitHub and os.replace()s the running
PyInstaller-onefile binary in place, atomic on the same filesystem;
is_available() gates this to a packaged Linux install, checking
sys.frozen and sys.platform), web/backend.py (new POST /update-now route,
index() now reads back updated/update_error query params for the
success/failure flash), web/templates/home.html (Update Now button +
confirm() dialog, success/failure banners). tests/test_self_update.py (7
new tests), tests/test_web.py (2 new tests).
NEW: HOME PAGE NOW CHECKS FOR A NEWER RELEASE, AND SHOWS A SHORT NOTE ABOUT WHAT CHANGED IN YOUR CURRENT VERSION
The home page now checks GitHub for a newer NuMa release each time it loads (cached for a few hours so it isn't re-checked on every visit) and shows an UPDATE AVAILABLE banner with a link to what's new when one exists. The version line at the bottom of the home page also now carries a short plain-language note about what changed in that build (e.g. "minor problem fixes"), instead of just the bare timestamp. The check fails silently if you're offline or GitHub is unreachable — it never delays or blocks the home page from loading.
Scope: version.py (new VERSION_NOTE constant, hand-updated alongside
VERSION and the Appendix A entry it summarizes), numa_app/services/
update_check.py (new — check_for_update() against GitHub's latest-release
API, string-compares the "vYYYY-MM-DD-HHMM" tag format scripts/create_
release.py already uses, in-process cached for 6 hours, never raises),
web/backend.py (index() route calls it via run_in_threadpool so a slow/
offline check can't block the event loop), web/templates/home.html (new
banner + version-note display). tests/test_update_check.py (7 new tests),
tests/test_web.py (1 new test), tests/conftest.py (no_update_check autouse
fixture stubs the network call for every other test, same pattern as
no_off/no_cnf).
MANUAL: "USING THE WEB APP" MOVED RIGHT AFTER THE INTRODUCTION, AND A STALE READING-TIME FIGURE FIXED
Part 6 ("Using the Web App") now comes right after Part 2 (the introduction), as the new Part 3 — the practical how-to-operate-NuMa material now reads before the nutrition-concepts and reference parts, instead of after them. Parts 3–6 renumbered accordingly (old 3→4, 4→5, 5→6), and every cross-reference to a part number throughout the manual was updated to match. Separately, the manual's "Reading time" figure had been silently wrong for a while — undercounting by close to 20,000 words — because the script that computes it treated any three backtick characters anywhere in the text as a code-fence marker, so a single sentence in Part 3 that mentioned the triple-backtick JSON fence syntax by name was misread as the start of a code block, and everything up to the next real fence (thousands of words) got wrongly excluded from the count. Reading time is now the corrected ~4 hours 19 minutes, not the ~2 hours 56 minutes shown before.
Scope: user-manual.md (Parts 3-6 reordered/renumbered, all in-text "Part N"
cross-references updated via a mapping pass, one prose line reworded to
drop the raw backtick sequence that broke word counting), scripts/build_
manual.py (count_words()'s fenced-code-block regex now requires the ```
fence to be alone at the start of its own line, per CommonMark, instead of
matching any three backticks anywhere in the raw text).