Skip to content

[compass] Fix: the uninstaller points at the anode.exe it just removed, and names only some of what it leaves - #26

Merged
skulitom merged 5 commits into
mainfrom
compass/anode-fix-2026-10-07-uninstall-leftovers
Oct 8, 2026
Merged

skulitom merged 5 commits into
mainfrom
compass/anode-fix-2026-10-07-uninstall-leftovers

Conversation

@skulitom

@skulitom skulitom commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Report

B-010 in Compass's ANODE-BUGS.md (filed 2026-10-03 by compass-followup, from C-024's sales gate: the release's own uninstall.ps1 -Quiet on a scratch install of 0.11.1, read by an independent customer review).

Root cause

The uninstaller's closing lines (scripts/uninstall.ps1, 140 to 142 on main) were written before the seat kept anything outside %LOCALAPPDATA%\Anode:

Fix

  • After removal, Get-Leftovers reads what really stays, without anode.exe, and the closing message lists each item with how to remove it without Anode:
    • each Anode state folder that exists (%LOCALAPPDATA%\Anode and any Packages\<app>\LocalCache\Local\Anode, found as rendering --restore finds them), and before it a rendering backup still in that folder: with its saved value and the by-hand restore while the preference is still Anode's 2, or marked out of date when it isn't;
    • the seat profiles that exist, said to be still signed in;
    • the copies of agent client settings connect-agents.ps1 saved before changing them (config.toml.anode-backup-*, .claude.json.anode-backup-*, this removal's included), which can hold other servers' tokens;
    • child sessions, read with WTSIsChildSessionsEnabled through Add-Type; if that can't be read (for example in Constrained Language Mode), the old "if you ran anode setup" wording;
    • the Remote Desktop host (fDenyTSConnections = 0), with Settings > System > Remote Desktop, hedged for a host that was on before setup;
    • DWMFRAMEINTERVAL = 15 and bEnumerateHWBeforeSW = 1, each named, with the existing SECURITY.md steps.
  • Each probe that fails is skipped; the list never fails a removal that has already happened. When another Anode is on the machine (a running anode.exe, or one on PATH), the list says these are shared with it.
  • A rendering restore that fails during removal now gives a warning; the backup it left is then in the list.
  • SECURITY.md: a new "Turning child sessions off without Anode" (an administrator PowerShell Add-Type call to WTSEnableChildSessions(false), the call setup --undo makes after signing the seat out), and a by-hand rendering restore from rdp-rendering-backup.json. INSTALL.md links the first. CHANGELOG entry added.
  • test-install.ps1 (CI's install step) runs the uninstaller with a stand-in %LOCALAPPDATA% holding a state folder, a packaged one with a rendering backup, and AnodeChrome, and checks that the closing message names each and no longer says setup --undo.

How it was tested

  • FOCUS test command (scripts\build.ps1 -QuickTest -OutputDirectory artifacts\pkg-build): pass, 93/93, docs links and anchors consistent, at each commit.
  • Sandbox runs of the branch's uninstall.ps1 -Quiet in Windows PowerShell 5.1, on a fake installation and a stand-in %LOCALAPPDATA% in scratch (the marker's shortcut and registry key don't exist, so nothing outside the sandbox changed): exit 0 in about 1 s, the user's file kept, user PATH unchanged. The list named the backup with its value, both state folders, AnodeChrome and AnodeAndroidStudio (not the absent AnodeEdge), and this PC's real child sessions, Remote Desktop host and frame-cap and GPU values.
  • test-install.ps1's new setup and assertion, replayed on their own with an 8.3 short workspace path, as on GitHub's runners (RUNNER~1). That caught two CI-only traps before pushing: Get-ChildItem spells a short path out in full, and Windows PowerShell 5.1's New-Item refuses nested folders under an 8.3 path. The test handles both. On this PC the replay also showed the "shared with another Anode" heading (the installed 0.11.0 is running) and listed the real config copies in the user profile (names only).
  • SECURITY.md's snippets: the rendering restore ran against a throwaway HKCU key. Value 2 with PreviousValue 0 gives a DWORD 0; 2 with null removes the value; a value changed later (5) is left alone; the backup is deleted each time; the key was removed afterwards. The child-session command was compiled and its signature checked, not called, because it changes a machine setting.
  • test-install.ps1 itself wasn't run here (this routine runs only the quick checks); this PR's CI runs it. CI failed once, on 7870fbb: the runner's own rendering value isn't Anode's 2, so the uninstaller rightly called the stand-in backup out of date, while the test expected the "set it back" wording, which holds only where a daemon has run. 359d3c0 checks only the backup's path, which both wordings name. So CI has now run both branches of that line, and this PC the other.

Second review

A fresh read-only Claude subagent (Codex is at its usage limit until Fri 10-09) found no P1. It ran the extracted Get-Leftovers under $ErrorActionPreference = 'Stop' for a Packages folder that denies listing, Constrained Language Mode and an unset LOCALAPPDATA: each ends with exit 0. Fixed in 7870fbb:

  • P2: the by-hand rendering restore didn't check that the value is still Anode's 2, so a stale backup could overwrite a newer value. The snippet now checks it, as rendering --restore does. The list marks such a backup out of date and shows each backup's saved value.
  • P2: with another Anode still installed, the list advised removing shared things. It now says they are shared with that Anode, and the child-session line says "with no Anode running".
  • P3s:
    • the client-settings copies are listed;
    • packaged folders are found with Get-ChildItem -LiteralPath (no wildcard surprises with [);
    • a missing LOCALAPPDATA skips only the folder lines;
    • the Remote Desktop line hedges for a host that was on before setup;
    • the frame-cap and GPU values are named;
    • SECURITY.md says what False means and to restart first if a seat may still be signed in.
  • Left: hidden Anode-SeatLaunch-* scheduled tasks after a failed DeleteTask. That is a rare product fault, not a leftover the script can see cheaply.

A second pass on 7870fbb found no open P1 or P2. It ran the functions in Windows PowerShell 5.1 with brackets in a path, no LOCALAPPDATA, no USERPROFILE and an invalid CODEX_HOME: nothing threw. It confirmed the CI fix in 359d3c0. Fixed in 50e6977:

  • P3: pasted with a wrong backup path, an empty file or a $previous left from an earlier paste, the by-hand restore could delete the value instead of restoring it. It now reads the backup into one object and acts, and deletes the backup, only when it read one and the value is still 2. Checked on a throwaway key in five cases: both saved values, a value changed later, a missing backup and an empty one.
  • P3: the list says "unreadable" for a backup it can't read, and "none (the value was absent)" as rendering --restore does.
  • P3: with another Anode present, the heading says it still uses the logs, profiles and machine settings among the items, which leaves out the client-settings copies.
  • Left: the SKILL.md.anode-backup-* copies of the skill file; they hold no tokens.

Manual check in a seat

None needs a seat. Worth one look on a real machine at the next release: uninstall from Settings > Apps after anode setup, and read the list.

🤖 Generated with Claude Code

skulitom and others added 3 commits October 7, 2026 23:28
…xe (B-010)

After removing anode.exe, the uninstaller pointed at SECURITY.md, whose answer is
'anode setup --undo', to turn child sessions off, and named only %LOCALAPPDATA%\Anode. It now ends
with what really stays, read without anode.exe: Anode state folders (a packaged app's too) and any
rendering backup in them, the seat's browser and Android Studio profiles (still signed in), child
sessions (WTSIsChildSessionsEnabled via Add-Type), the Remote Desktop host and the --fps 60/--gpu
values, each with how to remove it. A failing probe is skipped, never failing a finished removal.
A rendering restore that fails during removal is now reported instead of ignored.

SECURITY.md shows how to turn child sessions off and restore the rendering preference without
anode.exe; INSTALL.md links it. test-install.ps1 checks the list with a stand-in %LOCALAPPDATA%.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…B-010)

GitHub's Windows runners have a short TEMP (C:\Users\RUNNER~1\...). The uninstaller finds packaged
state folders through a wildcard, which spells the path out in full, so the test now expects the
packaged folder the same way. Windows PowerShell 5.1's New-Item also refuses nested folders under
an 8.3 path ("is not a subdirectory of"), so the stand-in folders are made with
[IO.Directory]::CreateDirectory. Both were replayed here with a short workspace path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The by-hand rendering restore, like 'anode rendering --restore', changes the value only while it
  is still Anode's 2, and the list says a backup is out of date when it isn't, with each backup's
  saved value.
- When another Anode is on the machine (a running process or anode.exe on PATH), the list says the
  items are shared with it; the child-session line says "with no Anode running".
- The copies of agent client settings connect-agents.ps1 saved, which can hold other servers'
  tokens, are listed.
- Packaged folders are found with Get-ChildItem -LiteralPath, as rendering --restore does; a missing
  LOCALAPPDATA skips only the folder lines; the Remote Desktop line hedges for a host that was on
  before setup; the frame-cap and GPU values are named separately.
- SECURITY.md: what False means, and restart first if a seat may still be signed in.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-07T22:43:40.249769Z 7870fbb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

skulitom and others added 2 commits October 7, 2026 23:45
… depends on the machine (B-010)

CI failed on 7870fbb: the runner's own rendering value isn't Anode's 2, so the uninstaller rightly
called the stand-in backup out of date, while the test expected the "set it back" wording, which
holds only where a daemon has run. Both wordings name the backup's path, which is what the test
now checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- SECURITY.md's by-hand rendering restore reads the backup into one object and acts only when it
  read one and the value is still 2, deleting the backup only then. A wrong path or an empty file,
  or a $previous left from an earlier paste, can no longer delete the value instead of restoring
  it (checked: missing, empty, changed-later and both saved values).
- The list says "unreadable" for a backup it can't read, and "none (the value was absent)" as
  rendering --restore does.
- With another Anode present, the heading says it still uses the logs, profiles and machine
  settings among the items, which no longer takes in the client-settings copies.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@skulitom

skulitom commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

Compass review: ready to merge

Tested by compass-merge on 2026-10-08 on head 50e6977, with the current main merged in locally (0d637d0, after tonight's merges of #23, #24 and #25; local merge tree 6819639, never pushed).

  • scripts\build.ps1 -QuickTest: Release build, 95/95 quick checks, documentation links, anchors and tool names consistent (this covers INSTALL.md's new link to SECURITY.md#turning-child-sessions-off-without-anode).
  • CI: build green on 50e6977, including scripts/test-install.ps1 and its new "uninstall lists what stays" assertion. I didn't re-run that script here: none of tonight's other merges touches scripts/, so CI's run covers the same install and uninstall code.
  • Review of the whole diff:
    • Get-Leftovers runs after removal, and every probe is guarded (registry reads, the JSON read, Add-Type, the client-settings scan), so a failed probe drops a line instead of failing the uninstall.
    • The rendering lines follow BackgroundRendering.Restore: the "not restored" advice only while the value is still 2, otherwise "out of date"; a missing PreviousValue reads as absent, as in the C# record.
    • The registry values match SECURITY.md's table (fDenyTSConnections 0, DWMFRAMEINTERVAL 15, bEnumerateHWBeforeSW 1).
    • SECURITY.md's by-hand restore changes nothing unless the value is still 2 and the backup reads.
    • The failed rendering --restore warning reads $LASTEXITCODE straight after the call.
    • No secrets: it prints the paths of client-settings copies, never their contents.
  • Second opinion: the Codex CLI is at its usage limit until Fri 10-09 22:15, so no codex-review.sh run. The fixer's two read-only Claude subagent passes found no open P1/P2 after 7870fbb and 50e6977, and the Codex GitHub review of 7870fbb finished with no findings.

@skulitom
skulitom merged commit 63a80f8 into main Oct 8, 2026
1 check passed
@skulitom
skulitom deleted the compass/anode-fix-2026-10-07-uninstall-leftovers branch October 8, 2026 00:25
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