Skip to content

Lot 15: detect the games marked by hand, and make the idle look cheap - #10

Merged
geeooff merged 12 commits into
mainfrom
lot-15-marked-games
Sep 23, 2026
Merged

geeooff merged 12 commits into
mainfrom
lot-15-marked-games

Conversation

@geeooff

@geeooff geeooff commented Sep 23, 2026

Copy link
Copy Markdown
Owner

Windows never starts the Game Bar presence writer for a title the person marked with Remember this is a game, so the watcher never saw those games. It does now — from their launch, or from the moment the box is ticked mid-game — and the idle look that finds them costs a twelfth of what it did.

What changed for the person running it

  • Games marked by hand are detected like any other, same commands at both ends. The log names them (Game detected: X, which is marked as a game by hand in the Game Bar), so a program ticked by mistake can be found and unticked.
  • At start, one log line per game marked by hand that Microsoft's own list now knows, so the box can be unticked and Windows recognise the game by itself. status lists the games marked by hand, each with whether it runs and whether Microsoft knows it.
  • The watcher at rest: 0.026 % of one core against 0.324 % for 0.2.0, measured nine minutes each on the same machine; private memory and handles at start unchanged (2.0 MB / 163 against 1.9 MB / 157).

How it was decided — docs/design/15-marked-games.md

  • The finding, 2026-09-20: DS2 ran for an hour, the watcher silent. Its registry entry was hand-made (Revision 1, no Xbox TitleId); the writer — Xbox presence — never starts for those.
  • Three probe runs (presence-probe watch-games, watch-methods) showed Windows writes the game's entry at every launch, and that RegNotifyChangeKeyValue never fires for the Game Bar's writes from an ordinary process, whichever of eight ways it is asked; WMI's RegistryTreeChangeEvent does. WMI was set aside by the maintainer until the reason is known; Microsoft's documentation names RegRestoreKey as the one blind spot, not confirmed.
  • A hand-made entry shadows Microsoft's: DS2 and Wreckfest 2 were in KnownGameList.bin (August, revision 2691, GUIDs matching the entries Windows creates from it); unticked, Windows recognised them. That is what the start hint is for.
  • Process ids, from Microsoft's documentation first: an id is unique only while its process lives; a spike measured the soonest reuse at 2.8 s under fifty spawns a second. So the look lists ids alone (K32EnumProcesses, 32 µs), names only new ones (52 µs each), and keeps the Toolhelp snapshot (4 ms) every thirty seconds as the net for a reused id.

How it is built

  • sensor::Sighting — the writer, or a hand-made game with its pid and exact signal; the engine parks on either's handle, does not refine a game marked by hand, and accepts either coming back within the grace. An untick mid-game takes effect at the next launch: noticing it would mean waking during the game.
  • detect::hand_made (entries, and the list key's last-write time, re-read only when it moves), detect::process::Tracker (the cheap look), detect::microsoft_list (the undocumented file, read for whole fields only; a first version missed DS2 and was caught against the real file).
  • The rules are pure functions tested without Windows (sensor::sighting_among, hand_made::covered); three engine scenarios; a tracker test on a real child process. On a hosted runner sensor.rs 43 %, the new modules 48–98 %; on a client machine the library is at 72.8 %.

Verified in the field, 2026-09-23

The Other Side from launch and from a tick mid-game, an untick mid-game leaving the session to end with the game, Starfield through the writer unchanged, the idle cost against 0.2.0. presence-probe footprint shows the lot's own memory and handles are nil; reading the GPU counters for the refinement (since Lot 3) keeps 3.7 MB after the first session — left for a separate piece of work.

For the reviewer

  • AGENTS.md gains Microsoft's documentation first, then measure and Cheap at rest, silent in game, the rule of splitting a check from its system call, and three pitfalls from this lot.
  • presence-probe gains watch-games, watch-methods, cost, footprint, microsoft-list — instruments, not shipped behaviour.

🤖 Generated with Claude Code

geeooff and others added 12 commits September 20, 2026 15:34
Death Stranding 2, marked "Remember this is a game" days earlier, ran
for an hour on 2026-09-20 with the watcher silent: Windows recorded the
game (LastGameActivity and the entry's LastAccessed three seconds after
the launch) and never started the presence writer. Its list entry has
Revision 1 and no Xbox TitleId, where every title the watcher has ever
detected carries the distributed revision and a TitleId; the writer is
presence, and a title named by hand has none to write. The detection
page keeps the figures and corrects its promise in place; How it works
and Getting started say what the toggle does and does not do for this
program. Lot 15 proposes reading Windows' own list as a second signal --
at launch and at the tick mid-game, on a registry change notification
rather than a poll -- with the measurements to make first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Lot 15's first box. `presence-probe watch-games` parks on a registry
change notification over Windows' game list and says, at each wake,
which entry was added, removed, or had its LastAccessed moved, labelled
hand-made or listed, with the presence writer logged beside it. An
optional key lets the probe be checked on a key one can write to; done
on a scratch key under HKCU\Software, which also showed that an entry
created with its values arrives as two notifications. `registry::Key`
gains a QWORD reader and its raw handle for the one call the wrapper
does not cover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The maintainer played five titles with the probe watching. DS2 and
Wreckfest 2 were in Microsoft's list all along: unticked and relaunched,
each got a new entry from the distributed list (Revision 2691, a
TitleId) and the presence writer started. The hand-made entry had been
shadowing Microsoft's. The Other Side, not listed, stayed unseen. So the
class splits: titles ticked before Microsoft listed them, which
unticking brings back today -- said now in How it works and Getting
started -- and titles Microsoft does not list, which only Lot 15
reaches. Windows writes the game's entry at every launch, listed or not,
within the same quarter-second as the writer. But the registry
notification never woke the probe through those writes; the probe now
arms once per wake and reads the list every 250 ms to log any change
that arrives without one, for a second run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seven writes to Windows' game list -- Starfield's launch, three launches
of The Other Side, an untick and a tick mid-game -- and not one change
notification; the probe found each by reading the list every 250 ms. The
lot's plan to park on RegNotifyChangeKeyValue is contradicted in place,
and replaced by what the runs decide: no new polling -- the second
signal reads the process snapshot the idle poll already takes, matching
hand-made entries by exact path -- and the list is read again only when
its key's last-write time moves, which a tick or an untick does. Three
measurements left before building.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`presence-probe cost` times the idle poll's steps over 300 rounds: the
process snapshot the poll already takes is about 4 ms, the hand-made
entries' names compared against it 13 us, the list key's last-write time
11 us, and a full-path query, paid only for a name that matches, 55 us.
The second signal adds about 25 us to a 4 ms poll and nothing that wakes
the watcher. FanControl, elevated through its task, granted SYNCHRONIZE
and the limited query to an unelevated reader, so an elevated game can
be waited on. The measurements box of Lot 15 is ticked; the untick
mid-game is left to the field run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On the maintainer's question -- was the notification the wrong one, or
asked the wrong way? -- `presence-probe watch-methods` armed eight
RegNotifyChangeKeyValue variants at once: through HKEY_USERS\<sid>,
through RegOpenCurrentUser, without the subtree, on the parent key and
on all of HKCU\System with every filter, on each entry on its own, and
synchronously on a blocking thread. A ninth, WMI's
RegistryTreeChangeEvent, ran from an unelevated PowerShell. Across a
Starfield launch and a tick and untick of The Other Side, WMI fired for
every write, as it happened; the eight fired once between them, the
removed entry waking its own handle. The record keeps the table and the
WMI provider's cost as far as an unelevated reader can see it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The maintainer set WMI aside until the reason is known and asked for the
literature. Microsoft's page for RegNotifyChangeKeyValue says it cannot
see changes made by RegRestoreKey; Raymond Chen explains why a deleted
key ends its notifications, which accounts for the removed entry and not
for Starfield; WindowsAppSDK issue 4075 reports the same silence the
other way round, for a packaged reader. Two documented mechanisms fit --
a restore, or a write through a packaged app's merged registry -- and
neither is confirmed; Process Monitor would show which.

Asked whether a longer interval would spend less: the probe's `cost`
now times K32EnumProcesses, the process ids alone, at 32 us against the
Toolhelp snapshot's 4 ms. A poll that names only processes it has not
seen would cost about 50 us, so the interval need not move; process id
reuse is the risk to measure first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Process ids: Microsoft documents that an id is unique only while its
process lives and can be reused after, and Raymond Chen that the id
lasts as long as the process object -- running, or held by a handle.
Nothing documents how soon; a spike of 400 short processes at fifty a
second saw ids come back after 2.8 s at the soonest, never within 2 s.
So the idle poll lists the ids alone, names only processes it has not
seen, and keeps a full snapshot every thirty seconds as the net for a
reused id: about 0.016 % of a core at two seconds against 0.2 % today.

The hint the maintainer asked for: Microsoft's list is the undocumented
KnownGameList.bin under the local profile; matched as whole fields --
the executable's name and its folder -- it knows DS2 and Wreckfest 2 and
not chrome.exe, and the GUIDs it carries are those of the entries
Windows created from it. At start, each hand-made entry it covers gets
one info line saying the box can be unticked; a file that does not
parse as described gives no hint.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lot 15's program side. The idle look now answers a Sighting: the
presence writer, or a process whose full path is a hand-made entry of
Windows' game list (Revision 1, no TitleId) -- the titles for which
Windows never starts the writer. The engine parks on either's handle;
a game marked by hand is not refined, its entry names it exactly. The
list is read again only when its key's last-write time moves, so a tick
mid-game becomes a session at the next look; an untick takes effect at
the next launch, since noticing it would mean waking during the game.

The look itself costs a twelfth of what it did: K32EnumProcesses every
look, a name only for an id not seen before, the Toolhelp snapshot every
thirty seconds as the net for a reused id. At start, one info line per
hand-made entry Microsoft's own list (KnownGameList.bin, read for whole
fields only) now covers, so the box can be unticked; status lists the
games marked by hand. A first reading of the list wanted each folder
name's length echoed after it and missed DS2; the rule is now two
characters or more, with DS2's record as a byte-for-byte test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The maintainer's run, 13:36-14:26: The Other Side detected from launch
and from a tick mid-game, an untick mid-game leaving the session to end
with the game, Starfield through the writer unchanged. The installed
watcher at idle, nine minutes each against the published 0.2.0: 0.026 %
of a core against 0.324 % -- twelve and a half times less, measured on
the whole process. A first reading of 5.1 MB and 424 handles came after
four sessions; `presence-probe footprint` runs the watcher's steps one
by one and shows this lot's own cost is nil and no step leaks, while
reading the GPU counters for the refinement, there since Lot 3, keeps
3.7 MB from the first session on. That and the handles gained over the
first sessions are left for a separate piece of work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hand-made sighting carried its process id as an Option that a missing
value would have turned into 0, a wait that ends at once; it is a field
now. The game list's key, missing at start on a profile the Game Bar had
not written yet, is tried again instead of never. Microsoft's list is
decoded once into a List instead of twice per entry, names are compared
in lower case beyond ASCII, and a record is read over three long folder
names' worth instead of 600 bytes. The writer's path is lowercased once.

Two rules sat inside system calls: which process is a sighting, and which
hand-made entries Microsoft's list covers. Both are now pure functions --
sensor::sighting_among takes the path lookup as a parameter,
hand_made::covered takes the decoded list -- with tests on made-up
processes and records, a tracker test on a real child process born
between two snapshots, and the relaunch scenario checks both waits. On a
hosted runner sensor.rs goes from 8 % to 43 % of its lines, hand_made.rs
from 37 % to 48 %; on a client machine the library is at 72.8 %.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md gains two principles -- Microsoft's documentation before any
spike, and a reader of real data is not done until it has read the real
data; and the idle look as the program's only timer, measured on the
whole installed process against the last release -- the rule that a
check next to a system call is split from it and tested alone, and three
pitfalls: script string literals eating backslashes, a packaged host's
shell reading a stale private %APPDATA%, and the registry notification
that never comes for the Game Bar's writes. The changelog gives the idle
figures as measured, and the lot page records the review.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@geeooff
geeooff merged commit 97339b5 into main Sep 23, 2026
1 check passed
@geeooff
geeooff deleted the lot-15-marked-games branch September 23, 2026 13:21
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