Skip to content

Lot 13: updating — check, download, verify and install from the icon - #5

Merged
geeooff merged 10 commits into
mainfrom
lot-13-updating
Sep 18, 2026
Merged

geeooff merged 10 commits into
mainfrom
lot-13-updating

Conversation

@geeooff

@geeooff geeooff commented Sep 18, 2026

Copy link
Copy Markdown
Owner

Summary

Lot 13, updating: a newer release is found, fetched, verified and installed from the notification icon — or from gamemode-executor update — with the game session handed from the old watcher to the new one. Design record in docs/design/13-updating.md; the shape was agreed before a line was written and corrected in place where the field said otherwise.

  • The session is handed over, not closed. stop --handover posts WM_HANDOVER where WM_CLOSE means Quit; the engine runs nothing and leaves the marker open, and the next watcher looks for the presence writer before recovering: still running, it resumes the session — name, icon, the wait on the handle — with neither start nor stop commands run. Three scenarios in engine/tests.rs; measured mid-game with Starfield on both install paths. The package's one stop action becomes two: StopForUpgrade hands over, StopForRemoval restores.
  • update is the object the menu renders and holds no rule of its own: phases, events, the items each phase shows and the two expiries live in Machine, tested through events. The network is behind Feed — the latest release's redirect, a text file, a download — with WinHTTP as the one real feed and, in the tests, a listener of their own on 127.0.0.1 from the standard library answering the record's table. No test calls an external host, and AGENTS.md says so. The hash is CNG's SHA-256; a bad certificate was measured refused (12175); the network path was measured by hand against v0.1.0 and is repeated with update --check.
  • The installer is the updater. An installed copy runs the package quietly through a hidden shell and lets the package hand this watcher's session to the new one; an unpacked copy's shell waits for this process, expands the archive over the folder with the old executables kept as .old, and runs install-task. A copy counts as installed only when the upgrade code is registered and it runs from the package's folder — decided when asked, not at start, since the package starts the watcher before it registers the product (that one bit a field run and was replayed clean). pending.txt, result.txt and Windows Installer's own log tell the next watcher whether the update took.
  • The menu closes on a click, as every Windows menu does, so the outcome reaches the user as a silent notification from the icon, held back during quiet hours, only ever to answer a click — and the version that comes out of an update says it runs at its first start. One notification for the outcome, not two for the steps.
  • Both artefacts carry the same four files — the executables, LICENSE.txt, README.txt — with one readme for both ways in that links the documentation and the recipes for the exact commit. What Windows Installer knows about the program moves to package, which purge and update both ask.
  • Lot 14, Release notes people can read, is proposed with the maintainer's questions on who writes them.

Field verification, 2026-09-18

On the maintainer's machine, from a 0.0.9 build of this branch against the real v0.1.0: zip path and package path, each with the handover mid-game, the check, the download and its verification, the install, and the watcher back on 0.1.0 within a second; the notifications seen on screen, the first-start one with a planted pending file. What waits for the first real release pair: the chain where one version writes the pending file and the next reads it — the lot stays in progress until an update runs from 0.2.0.

One degradation, accepted and recorded: the installed 0.1.0 does not know --handover, so the upgrade from it gets the Restart Manager's dialog once, on the two machines that have it.

Test plan

  • .\scripts\build.ps1 test — 165 + 4 tests, 25 pages, every link resolves; line coverage 64.5 % → 70 %
  • .\scripts\build.ps1 release — 102 ICEs clean, both artefacts carry the four files
  • Field: both paths, handover mid-game, notifications, the kind decided when asked (replayed at 17:56)
  • CI

The version bump to 0.2.0 comes on its own release/0.2.0 branch after this merges, per the release procedure.

🤖 Generated with Claude Code

geeooff and others added 10 commits September 18, 2026 11:46
The page records what was decided on 2026-09-18 before any code: the
installer is the updater and the package already stops and restarts the
watcher; the zip copy updates itself through the after-exit shell; the
session is handed over rather than closed, with stop --handover and a
start that resumes an open session; the state machine the menu renders,
its phases, entries and two expiries; the faults and their log lines;
what it does not defend against; and what to measure.

AGENTS.md gains the three rules the lot rests on: the tray renders state
and holds no rule, the setup commands are a contract with three callers
verified together, and the one network connection is the user's click.
The non-goal and the README say the same.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A stop mid-game meant Quit: the stop commands ran, the marker went, and
the watcher that followed detected the game again and ran the start
commands -- the idle and then the gaming configuration two seconds
apart, in the middle of a game. An update or an upgrade needs a stop
that leaves the session open instead.

StopSignal carries a reason. `stop --handover` posts WM_HANDOVER to the
session window where WM_CLOSE means Quit; the engine then runs nothing
and leaves the marker saying the session is open. At start, the engine
looks for the presence writer before recovering: still running, the
session is resumed from the marker -- name, icon, the wait on the
writer -- with neither start nor stop commands run; gone, the recovery
of before. Three scenarios in engine/tests.rs.

The package's one stop action becomes two: StopForUpgrade hands over,
since RegisterTask starts the new watcher seconds later, and
StopForRemoval restores, since nobody follows an uninstall. The
installed 0.1.0 does not know the flag, so the first upgrade from it
gets the Restart Manager's dialog back once, accepted and recorded.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`update` is the object the menu renders and holds no rule of its own:
phases, events, the items each phase shows and the two expiries live in
`Machine`, tested whole through events. The network is behind `Feed` --
the latest release's redirect, a text file, a download -- with WinHTTP
as the one real feed; the hash is CNG's SHA-256. What the release
answers was measured by hand against v0.1.0 through the code itself,
and what the tests exercise instead is a listener of their own on
127.0.0.1, written from the standard library, answering the record's
table: the 302 with the tag, a redirect to a second listener for the
asset host, a body larger than one read, a cut-off download, a 404, a
503, a page where a text file should be, a port nobody listens on. It
talks plain http to a cfg(test) constructor the shipped program does
not have; TLS is WinHTTP's, and a bad certificate was measured refused
with 12175. No test calls an external host, and AGENTS.md says so. Two
things the record could not know: closing a WinHTTP session cancels the
requests under it, so the parent handles ride along with the response;
and a header is asked for its length first, since a signed asset URL
runs to a kilobyte.

An installed copy runs the package quietly through a hidden shell and
lets the package hand this watcher's session to the new one; an
unpacked copy's shell waits for this process, expands the archive over
the folder with the old executables kept as .old, and runs
install-task. A copy counts as installed only when the upgrade code is
registered and it runs from the folder the package installs to, so an
unpacked copy beside a package updates itself rather than the package.
pending.txt and result.txt tell the next watcher whether the update
took. What Windows Installer knows about the program moves to
`package`, which purge and update both ask.

The tray draws the section from update::view() and hands a chosen entry
back; `gamemode-executor update [--check]` drives the same object from
a console. Every step is logged under `update`. The library's line
coverage goes from 64.5 % to 70 % with the updater at 77 % to 95 % per
file and the command line at 44 % from nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…p the same four files

What the first field run of the updater taught, both paths against the
real v0.1.0. The menu closes on the click, as every Windows menu does,
and the verdict waited in a menu nobody had reopened: the outcome of
what the user clicked now reaches them as a silent notification from
the icon, held back during quiet hours, and stays in the menu too. The
object owns it -- Machine leaves a Notice on each outcome and the tray
draws it -- and it only ever answers a click.

A 0.0.9 build installed after a 0.1.0 that did not know pending.txt
reported the update failed when it had not: settle now reads Windows
Installer's own verdict from the log it wrote, UTF-16 with a byte-order
mark, and says installed, or names the installer's error code, before
falling back to "did not take". The resume line carried a run of spaces
where a line continuation had been meant; fixed.

The package carried the license and two executables, the zip a readme
and the whole documentation tree: both now carry the same four files,
LICENSE.txt and README.txt beside the executables, one readme for both
ways in that links the documentation and the recipes for this exact
commit. Lot 14, release notes people can read, is proposed on the
maintainer's remark that the release page says nothing to the person
who clicked What changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The agent, from the commits and the design record, on request; the
workflow publishes a placeholder it cannot fill; the rules of the
summary go in AGENTS.md before anyone follows them twice. Left open in
Lot 14 until it is taken.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… when asked

The install goes by in a second on a fast machine, too quick to see the
version change: the version that comes out of it now says so with one
more notification at its first start, from the pending file the
previous watcher wrote.

A watcher the package itself starts decided at start whether it was an
installed or an unpacked copy -- and RegisterTask runs before
RegisterProduct, so at that instant Windows Installer knows no product.
On the second field run of the package path it took itself for an
unpacked copy, fetched the zip and expanded it over the package's own
folder. The kind is now decided when the question is asked, from what
Windows Installer says at that moment; the tests pin it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The version that comes out of an update says it runs at its first
start, from the pending file the previous one wrote. A notice for the
install step itself, shown for an afternoon, was one too many: one
notification for the outcome, not two for the steps. The published
0.1.0 predates the pending file, so the field runs ended on no
notification; the first real release pair will show the new one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The package header counts five actions, not four; the updater's module
doc names check_now as the one connection rather than the menu's action;
the user agent carries the bare version; how-it-works lists the updates
folder among the places; the lot page says it ships in 0.2.0 and closes
on the first update from it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The notes claimed Windows 10 and 11. Windows 11 is where every
measurement in the record was taken; Windows 10 ships the same Xbox
Game Bar component since 1903 and should work, and the notes now say
exactly that rather than more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@geeooff
geeooff merged commit e09cbf9 into main Sep 18, 2026
1 check passed
@geeooff
geeooff deleted the lot-13-updating branch September 18, 2026 16:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant