emrg: upgrade banner — in-memory run version vs live installed_version, dedicated upgrade event (rant 2026-08-21T14:38:27) - #917
Conversation
…n, dedicated upgrade event (rant 2026-08-21T14:38:27)
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle
Fresh review of the upgrade-banner judgment refactor (rant 2026-08-21T14:38:27):
- Root causes addressed: (1)
previous-version.txtresidue previously made every launch re-show the banner; (2) livecurrent_versionread hid the "pending restart" state. - daemon:
self._run_versioncaptured once at startup; pong returnscurrent_version(in-memory run) +installed_version(live disk);_previous_installed_version()and pongprevious_versionremoved. - main.js: init baseline unchanged; heartbeat compares
installed_version !== current_version→ dedicatedupgradeevent (nostatusreuse, no handleStatus spam). - app.js:
case "upgrade"→maybeShowUpgradeBanner(installed, run)with from→to text +lastKnownVersiondedup; banner logic removed fromhandleStatus. - Tests cover all three states (show on installed≠run, hide on already-shown, status no longer triggers) + daemon run-vs-installed fixed/live semantics; no previous_version leakage anywhere.
- Verified locally: pytest 994 passed + 1 skipped (995 collected), GUI 257 (249+8), node --check main.js + app.js OK, import + CLI OK.
1/3.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle
2nd review (head ca93102, MERGEABLE/CLEAN, CI test + test-windows PASS):
- Fresh local verification on this branch: pytest 994 passed + 1 skipped (995 collected), GUI npm test 257 (249 pass / 0 fail / 8 skipped), import + CLI OK.
- daemon.py: _run_version captured once at startup (fixed for process lifetime); pong now returns current_version = in-memory run version + installed_version = live disk read; previous_version removed from the judgment (previous-version.txt no longer part of the model — addresses the residue flaw).
- main.js: init passes current_version as lastKnownVersion baseline; 15s heartbeat compares installed_version ≠ current_version → dedicated 'upgrade' event (not reusing 'status', avoiding handleStatus side-effect spam).
- renderer: handleEvent case 'upgrade' → maybeShowUpgradeBanner(installed, current) with lastKnownVersion dedup.
- Self-consistency matrix is correct: run==installed → no banner; installed≠run → banner; after restart-to-apply run==installed → gone; dismiss+relaunch without daemon restart → banner again (correct).
2/3.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle
3rd review (head ca93102, CI test + test-windows PASS on run 32457292037):
- Fresh local verification: Python pytest 994 passed + 1 skipped (995 collected), GUI npm test 257 (249 pass + 8 skipped),
node --checkmain.js + app.js OK, import OK. - Model verified sound:
self._run_versionfixed at daemon startup; pongcurrent_version= run version,installed_version= live disk; heartbeat emits dedicatedupgradeevent on mismatch; renderercase "upgrade"shows from→to withlastKnownVersiondedup;previous_versionfully removed from judgment path. - New daemon test proves
_run_versionstays fixed while the disk read reflects a later upgrade, and pong carries noprevious_version.
3/3 — merging.
Uh oh!
There was an error while loading. Please reload this page.
Upgrade banner — in-memory run version vs live installed_version + dedicated
upgradeeventRant 2026-08-21T14:38:27: the banner judgment model had two logic flaws:
previous-version.txtresidue — the daemon's pongcurrent_versionread
version.txtlive andprevious_versionreadprevious-version.txt.The file stays at the pre-upgrade version forever (e.g. 0.2.61), so every
launch judged "version changed" and re-showed the banner even with no new
release.
current_versionreading the filein real time meant that after a daemon restart the GUI saw the new version
and could not distinguish "already applied" from "needs restart".
New model
self._run_version(the code version this daemon process actually runs, fixed for its
lifetime). Pong now returns:
current_version= in-memory_run_version(actually running)installed_version= live disk read (upgrade agent may have updated it)previous_versionremoved —previous-version.txtis no longer part ofthe judgment (the file may still be written by the upgrade agent).
current_versionto the renderer as thelastKnownVersionbaseline; the 15s heartbeat (from emrg: GUI daemon heartbeat — periodic liveness probe + active reconnect (rant 2026-08-21T12:44:34) #912) comparespong.installed_version !== pong.current_version(and installed non-empty)and emits a dedicated
upgradeevent (not reusingstatus, avoidinghandleStatusside-effect spam) with{current_version, installed_version}.handleEventgainscase "upgrade"→maybeShowUpgradeBanner(installed_version, current_version)— text"upgraded from {current} to {installed} — restart to apply" with the
existing
lastKnownVersiondedup (same installed version shown once).Self-consistency: no upgrade (run==installed) → no banner; upgrade installed
but not restarted (run 0.2.61 vs disk 0.2.62) → banner; after restart-to-apply
(new daemon, run==installed) → no banner; dismiss + relaunch GUI without
restarting the daemon → banner again (correct: not yet applied); further
upgrade 0.2.63 → banner again. No cross-session persistence needed.
Tests / verification
tests/test_daemon.py:test_pong_includes_current_versionasserts bothcurrent_versionandinstalled_version; newtest_pong_run_vs_installed_versionverifies_run_versionis fixed atstartup while
_current_installed_version()reflects a later disk write,and that pong no longer carries
previous_version.renderer.smoke.test.js: banner test rewritten for theupgradeevent —positive (installed≠run → from→to shown), negative (already shown version →
hidden), and a new negative asserting
statusno longer triggers thebanner; source-level asserts
case "upgrade"and the heartbeat'ssendToRenderer("upgrade")/pong.installed_version.257 (249 pass + 8 skipped),
node --checkmain.js + app.js OK, import +CLI OK. Test counts unchanged (Agent.md stays valid).