# Session Log — 2026-05-31 — BEAST: identity.json migrations + tooling rollout observed

## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-BEAST-ROG
- **Role:** admin

## Session Summary
Two coord-driven identity.json migrations were applied to GURU-BEAST-ROG over 2026-05-26/27, plus
routine syncs through 2026-05-30 that pulled the team's GuruConnect tooling rollout. No new
substantive workstreams from this machine — this log exists so future BEAST sessions can see when
the centralized-identity onboarding completed here and what tooling is now expected to be present.

**Phase 1 (2026-05-26)** — coord message from Mikes-MacBook-Air requested every machine add
`claudetools_root` to its local `.claude/identity.json`. Detected the path via
`git rev-parse --show-toplevel` (`C:/Users/guru/ClaudeTools`), added the field via an Edit (preserved
existing key order), validated the JSON parses, and replied via coord (`a266aa66`). The pulled
commit `f94849f feat(identity): read claudetools_root from identity.json` confirmed `sync.sh` and
`syncro.md` now consume the field, so BEAST is compatible.

**Phase 2 (2026-05-27)** — coord message requested running `bash .claude/scripts/migrate-identity.sh`
to add Python/platform/Ollama config. Reviewed the script before running (it does detect-and-merge,
no destructive ops); ran it, validated identity.json, and replied via coord (`6768c863`). The
detection set `python.command: py`, `platform: windows`, `architecture: amd64`,
`ollama.endpoint: http://localhost:11434` (Beast hosts Ollama locally), fallback Tailscale endpoint
`100.101.122.4:11434`, and `prose_model: qwen3:14b` (BEAST is not in the 12 GB-VRAM list that gets
qwen3:8b). The 2-second Ollama curl probe per Tier-0 call is now eliminated on BEAST.

**Syncs through 2026-05-30** pulled the team's broader expansion of the audit/feature-request
tooling — a new `/gc-audit` skill (GuruConnect end-to-end audit, 7 passes), a new `/rmm` skill (run
commands and remote scripts via the agent fleet), `/mailbox` (M365 mailbox), and `/gc-feature-
request`; plus the `post-bot-alert.sh` helper grew prefix-based routing between `#bot-alerts` (Syncro
+ general) and a new private `#dev-alerts` (`1509998508198068484`, RMM/Dev only). Wiki additions:
`barbaragrygutis.md`, `lonestar-electrical.md`, `quantumwms.md`; updates to `gururmm.md`,
`pluto.md`, `birth-biologic.md`, `cascades-tucson.md`. The `.gitignore` now also excludes
`.claude/commands/autotask.md` (Syncro stays the default PSA; `/autotask` is opt-in).

## Key Decisions
- **Read `migrate-identity.sh` before running it** — it's a config-writing script from another
  session. Confirmed it's a safe detect-and-merge (no destructive ops, preserves prior fields)
  before executing on this machine.
- **Did not commit `identity.json`** — it's gitignored by design (machine-local). Both Phase 1 and
  Phase 2 changes stay on BEAST only.
- **Used the deterministic `whoami-block.sh`** for the User block per the new `/save` flow rule.
  Output emitted no `[WARNING]`, confirming identity.json/users.json/hostname agree on BEAST.

## Configuration Changes
- `.claude/identity.json` (gitignored, BEAST-local) — manual Phase 1 edit (added `claudetools_root`),
  then `migrate-identity.sh` Phase 2 (added `python`, `ollama`, `platform`, `architecture`,
  `last_updated`). All earlier fields preserved.
- No tracked files modified by this machine this session. All other changes (Phase docs, tooling,
  wiki) were pulled from team commits, not authored here.

## Credentials & Secrets
- None created, rotated, or discovered this session. Existing build-server SSH password remains at
  `infrastructure/gururmm-server.sops.yaml` field `credentials.password` (used read-only in the
  prior session; unchanged here).

## Infrastructure & Servers
- **This machine:** GURU-BEAST-ROG (Windows / amd64). Hosts Ollama locally on `localhost:11434`
  (Tailscale fallback exposed at `100.101.122.4:11434` for other machines).
- **Discord:** new private alerts channel `#dev-alerts` id `1509998508198068484` (RMM/Dev,
  Howard + Mike); existing `#bot-alerts` id `624710699771232265` remains the default.
- **Coord API:** `http://172.16.3.30:8001/api/coord` (unchanged).
- **gururmm:** no work this session. The earlier audit-2 remediation branch
  `fix/audit-2-remediation` is still pushed and unmerged at gururmm origin (per
  `2026-05-25-beast-gururmm-audit-2-remediation.md`).

## Commands & Outputs
- `git rev-parse --show-toplevel` → `C:/Users/guru/ClaudeTools` (Phase 1 path detection).
- `bash .claude/scripts/migrate-identity.sh` (Phase 2) — detected `py` / windows / amd64 / local
  Ollama / qwen3:14b; merged into identity.json.
- `bash .claude/scripts/whoami-block.sh` — clean (no `[WARNING]`).

## Pending / Incomplete Tasks
- None from this machine. Open threads from the prior session remain: gururmm
  `fix/audit-2-remediation` awaiting Mike's merge; `update_rollouts` (a) Phase-2 re-spec; Mac-agent
  product decision (A ship vs B defer + quiet the freshness check); MacBook deploy go/no-go.

## Reference Information
- Coord replies sent this session: `a266aa66` (Phase 1 claudetools_root), `6768c863` (Phase 2
  migration complete).
- New skills observed (pulled, not authored here): `/gc-audit`, `/rmm`, `/mailbox`,
  `/gc-feature-request`.
- `post-bot-alert.sh` channel routing: `[RMM]/[DEPLOY]/[DEV]/[BUILD]/[GURURMM]/[SMARTBADGE-WATCH]`
  prefixes auto-route to `#dev-alerts`; everything else (incl. `[SYNCRO]`) stays in `#bot-alerts`;
  overridable via 2nd arg (`dev`/`bot`/raw channel id).
- Pulled commits of note: `f94849f` (identity field consumers), `6c4c17a` (migrate script),
  `251bb35` (Windows-compat fixes), `c70cd70` (gc-audit skill), `df6a2dd` (GuruConnect v2 direction
  memory), `e8ac759` (SPEC-017 mobile/Apple cert).

---

## Update: 19:20 MST — Crash recovery, onboarding-diag hardening, GuruRMM PR #29 merge

### User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech

### Session Summary
Resumed after a mid-task crash during "minor fixes for GuruRMM." First established that no work was lost: the GuruRMM quick-wins branch (`fix/quickwins-sitedetail-ux` @ fd9bc09) was already committed and pushed to origin as open PR #29, and the only uncommitted artifact was an in-progress edit to the root-repo onboarding diagnostic plus two crash-artifact JSON files. Removed the artifacts (`pr33.json`, `prs.json`).

Reviewed and hardened the uncommitted `onboarding-diagnostic.ps1` change (3rd-party-AV detection that downgrades two Defender findings from critical to info). Code Review Agent returned APPROVE WITH NITS and flagged two real false-positive holes; both were fixed before commit. Committed (b1a5bc9), rebased onto 4 incoming GURU-5070 commits, repushed as 959b3a1.

Investigated GuruRMM PR #29: open, mergeable, no branch protection, no CI/status checks, unreviewed. Ran the dashboard diff through Code Review Agent (APPROVE WITH NITS — only nit a pre-existing server COALESCE limitation). Merged PR #29 via Gitea internal API (merge commit 5e391ca; CI then auto-bumped to 529b0b2), deleted the head branch, advanced the submodule working tree to main, and bumped the root submodule pointer (root commit 9d21c23, pushed).

Routed the still-pending dashboard build/deploy to Mike (coord message to GURU-5070/claude-main + durable todo assigned to mike) since the deploy runs from his machine. Created a follow-up todo for the notes-clear limitation.

### Key Decisions
- Left the GuruRMM submodule pointer uncommitted in the root repo until PR #29 actually merged — bumping the parent to a non-main feature-branch commit would be wrong.
- Pushed root `main` directly rather than via `/sync`, because `/sync` stages the submodule pointer (which we deliberately wanted to leave alone until merge).
- Fixed the two review nits before committing the onboarding-diag change rather than tracking them as follow-ups, because both actively suppress *critical* AV-coverage alerts in common fleet scenarios (Datto RMM present; lapsed OEM AV).
- Merged PR #29 as a standard merge commit (not squash); nothing gated it after a clean review.
- Did NOT mark the gururmm/dashboard coord component as deployed — the merge is to main only; no build/deploy occurred. Handoff sent to Mike instead.

### Problems Encountered
- Root `git push` initially rejected: origin/main had advanced (4 GURU-5070 commits). Resolved by fetch + verifying the incoming commits did not touch our file, then a clean rebase (handled by Gitea Agent, preserving the unstaged submodule gitlink).
- Coord API POSTs failed repeatedly: todos require `text` (not `title`) plus `created_by_user`/`created_by_machine`; messages POST 400'd on inline multi-line `-d` JSON (shell quoting) and succeeded via `--data-binary @-` heredoc. A stray "test" message was marked read to keep it out of Mike's unread queue.

### Configuration Changes
- Modified + committed: `.claude/scripts/onboarding-diagnostic.ps1` (root commit b1a5bc9 -> rebased 959b3a1) — 3rd-party-AV detection now requires SecurityCenter2 `productState` RTP-enabled bit (0x1000); Datto fallback requires a Datto name AND an AV/EDR token and excludes RMM/Backup/Workplace/Continuity/File; corrected a misleading "reuse data" comment.
- Submodule pointer bumped: `projects/msp-tools/guru-rmm` 6f31d22 -> 529b0b2 (root commit 9d21c23).
- Deleted (crash artifacts): `pr33.json`, `prs.json`.
- guru-connect submodule working tree advanced to 1601745 to match the pulled index (no commit).

### Credentials & Secrets
- Gitea API (non-browser) must use the internal endpoint `http://172.16.3.20:3000` (Cloudflare fronts the public hostname and blocks API calls). Howard's Gitea account uses basic auth; password vaulted at `services/gitea-howard.sops.yaml` field `credentials.password`. No new secrets created.

### Infrastructure & Servers
- Gitea internal API: `http://172.16.3.20:3000/api/v1` (repo `azcomputerguru/gururmm`).
- Coord API: `http://172.16.3.30:8001/api/coord` (messages, todos).
- GuruRMM dashboard live host: `https://rmm.azcomputerguru.com` (still serving v0.2.32 — PR #29 changes not yet deployed).

### Commands & Outputs
- `[Parser]::ParseFile(...)` on onboarding-diagnostic.ps1 -> PARSE OK (post-fix). Logic checks: productState 0x1000 set->active, clear->ignored; Datto EDR/AV->detected, Datto RMM/Backup->excluded.
- PR #29 merge: `POST /repos/azcomputerguru/gururmm/pulls/29/merge {"Do":"merge"}` -> merged:true; head branch DELETE -> 204.
- Root push: `959b3a1..9d21c23  main -> main`.

### Pending / Incomplete Tasks
- **Mike:** build/deploy gururmm dashboard (PR #29 changes) via build-server.sh, then bump coord component gururmm/dashboard. Coord msg 9b247556 + todo 929ce451.
- **Follow-up (Howard):** todo ab091bae — allow clearing Site notes (server COALESCE($3,notes) blocks empty; send explicit null for empty-string in sitesApi.update + server handler).
- **Unblocked next tier (existing todo 15a5440f):** BUG-009/010 isError handling, BUG-011 remove `any`, BUG-008 metrics internal_err, BUG-015 agent in Programs & Features.

### Reference Information
- PR #29: `azcomputerguru/gururmm` — merge commit 5e391ca, main tip 529b0b2 (CI version-bump).
- Root commits: b1a5bc9 -> 959b3a1 (onboarding-diag), 9d21c23 (submodule bump).
- Coord todos: ab091bae (notes-clear, howard), 929ce451 (deploy, mike). Coord message: 9b247556 -> GURU-5070/claude-main.

---

## Update: 19:48 MST — Deployed PR #29 dashboard to production via RMM agent

### User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech

### Session Summary
Deployed the merged PR #29 dashboard changes to production at rmm.azcomputerguru.com. Mike (GURU-5070) clarified two things over coord: (1) the deploy is not pinned to his machine — build-server.sh runs on the gururmm server (.30) itself; (2) build-server.sh builds only the Rust SERVER, while PR #29 is dashboard/frontend, which ships as static files under /var/www/gururmm/dashboard/. A server rebuild alone would not make the dashboard changes live, which is why it remained v0.2.32 after the 529b0b2 merge.

Howard-Home has no SSH key for guru@172.16.3.30 (only the vaulted password; no sshpass/plink for non-interactive password auth), and the vault has no SSH key for .30 (the OpenClaw fleet key is for Tailscale 100.x hosts only). Per Mike's "get it from the vault, OR add one via RMM," took the RMM path: the GuruRMM local agent runs on .30 as root, so the deploy was dispatched through it directly — no SSH needed.

Recon via the agent showed the server repo clone (/home/guru/gururmm) was already at merged main 529b0b2 (the push-to-main webhook had reset it), node v20.20.0 / npm 10.8.2 available, and /var/www root-writable. Deploy was therefore just build + publish: `sudo -u guru npm run build` then `rsync -av --delete dist/ /var/www/gururmm/dashboard/`. build-server.sh was deliberately skipped — PR #29 has no server changes, so rebuilding/restarting the prod server was unnecessary risk.

### Key Decisions
- Deployed via the on-box RMM agent rather than SSH: no key on Howard-Home, agent runs as root on .30, cleanest path and matches Mike's "via RMM" guidance.
- Skipped build-server.sh: PR #29 is dashboard-only; the server binary was unchanged, so a prod server bounce had no benefit.
- No git sync needed: agent recon confirmed the repo clone was already at 529b0b2 (webhook-managed via git reset --hard origin/main).
- Confirmed external curl of rmm.azcomputerguru.com returns empty (Cloudflare non-browser block) — relied on agent-side on-disk verification (live index.html asset == freshly built asset) instead.

### Problems Encountered
- Initial coord message to Mike wrongly stated build-server.sh deploys the dashboard; Mike corrected it. Re-sent an accurate resolution message.
- No SSH access from Howard-Home to .30; resolved by running the deploy through the RMM agent instead of SSH.

### Configuration Changes
- Production deploy on .30: rebuilt dashboard (dist/) and rsync --delete to /var/www/gururmm/dashboard/. Live bundle index-CUrUAhv_.js (was index-Di8C-nbq.js). No repo or server changes.

### Credentials & Secrets
- guru@172.16.3.30 SSH/sudo password: vault `infrastructure/gururmm-server.sops.yaml` field `credentials.password` (SSH and sudo are the same). No SSH key exists for .30 in the vault.
- RMM API admin creds: vault `infrastructure/gururmm-server.sops.yaml` `credentials.gururmm-api.admin-email` / `admin-password`. API base http://172.16.3.30:3001, JWT 24h.

### Infrastructure & Servers
- GuruRMM local agent on .30: hostname `gururmm`, os linux, agent id `5e5a7ebc-95ea-40c8-b965-6ec15d63e157`, runs as root, client "AZ Computer Guru".
- Dashboard served by nginx from /var/www/gururmm/dashboard/ on .30; deployed v0.2.34.

### Commands & Outputs
- Recon cmd 9bdc02b9 (exit 0): agent_user=root, can_sudo_guru=yes, node v20.20.0, npm 10.8.2, repo_HEAD=529b0b2, origin_main=529b0b2, live_asset=index-Di8C-nbq.js.
- Deploy cmd c49adad8 (exit 0): BUILD_OK, built_asset=index-CUrUAhv_.js, rsync sent ~1.38 MB, live_asset=index-CUrUAhv_.js, DASHBOARD_DEPLOYED_OK.

### Pending / Incomplete Tasks
- Follow-up todo ab091bae (howard): allow clearing Site notes (server COALESCE blocks empty).
- Unblocked next tier (todo 15a5440f): BUG-009/010 isError, BUG-011 remove `any`, BUG-008 metrics internal_err, BUG-015 agent in Programs & Features.

### Reference Information
- Coord: component gururmm/dashboard -> deployed v0.2.34; lock 7ec5ac68 released; todo 929ce451 (deploy) done; messages to GURU-5070/claude-main.
- #dev-alerts: dispatch + completion alerts posted (cmd c49adad8).
- Procedure ref: wiki/systems/gururmm-build.md:122-127 (server + dashboard deploy steps).

---

## Update: 20:10 MST — Fixed + deployed clearable Site notes (PR #33)

### User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech

### Session Summary
Fixed and shipped the notes-clear follow-up (todo ab091bae). Root cause: SiteDetail.tsx sent `notes: notes || undefined`, coercing an emptied notes box to undefined, which JSON-omitted the field; the server's `UPDATE sites SET notes = COALESCE($3, notes)` then kept the old value. Coding Agent verified the server already binds `Some("")` correctly (server/src/api/sites.rs:234 passes req.notes verbatim, no empty->None coercion), so the fix is client-only: send "" instead of dropping it. Two call sites changed (inline notes editor ~933 -> `{ notes }`; Edit Site modal ~1215 -> `notes: data.notes ?? ""`); other fields keep `|| undefined` partial-update semantics. Confirmed the third caller (Sites.tsx:387) is unaffected.

Code Review: APPROVE. Gitea Agent branched fix/clear-site-notes, committed 048306f, opened PR #33, merged (873e9ca), deleted branch; CI bumped to 342d3a1; root submodule pointer bumped (fa57fab) and pushed. Deployed via the gururmm RMM agent on .30 (synced repo to origin/main, npm build + rsync), exit 0, live bundle index-CiQNGkLY.js, dashboard v0.2.35.

### Key Decisions
- Client-only fix (no Rust change): server COALESCE already clears on Some(""); '' is the correct sentinel since the UI treats empty notes as "no notes."
- Deploy command syncs the repo clone explicitly (git reset --hard origin/main) rather than relying on the push-to-main webhook timing, and was prepared to re-run on "interrupted" since that webhook restarts the same agent used for RMM exec (it completed clean).

### Configuration Changes
- gururmm: dashboard/src/pages/SiteDetail.tsx (2 call sites). PR #33 merged to main.
- Production deploy on .30: rebuilt dashboard + rsync --delete to /var/www/gururmm/dashboard/. Live index-CiQNGkLY.js. v0.2.35.
- Root repo: submodule pointer bump fa57fab.
- Memory added: reference_gitea_api_credential.md (+ MEMORY.md index line).

### Credentials & Secrets
- Gitea API auth correction (now in memory): use `services/gitea-howard.sops.yaml` credentials.password as howard on internal http://172.16.3.20:3000 — NOT the gururmm-server SSH password (which the Gitea API rejected for the PR #33 merge).

### Commands & Outputs
- Deploy cmd f501d83f (exit 0): HEAD synced to 342d3a1, BUILD_OK, built_asset=index-CiQNGkLY.js, DASHBOARD_DEPLOYED_OK.

### Pending / Incomplete Tasks
- Unblocked next tier (todo 15a5440f): BUG-009/010 isError, BUG-011 remove `any`, BUG-008 metrics internal_err, BUG-015 agent in Programs & Features. (Not started.)

### Reference Information
- PR #33: merge 873e9ca; main tip 342d3a1; submodule branch commit 048306f.
- Root pointer-bump commit: fa57fab.
- Coord: component gururmm/dashboard -> deployed v0.2.35; lock 427369c8 released; todo ab091bae done.

---

## Update: 21:41 MST — Next-tier quick-wins: a11y fix deployed + BUG-015 ARP MSI shipped

### User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech

### Session Summary
Started the "next tier" roadmap quick-wins (todo 15a5440f) and found it largely stale: verified in current code that BUG-008 (metrics.rs leak), BUG-009/010 (isError handling), and BUG-011 (`: any` — now 0 dashboard-wide) are all already fixed. Only two items had real work left.

1) **a11y (LogAnalysis FindingCard):** the toggle button's `aria-controls={detailsId}` pointed at a conditionally-rendered panel, so the id didn't exist when collapsed. Fixed by always rendering the container with `hidden={!expanded}` (heavy inner content still gated by `expanded` inside). Reviewed APPROVE. Shipped as PR #34 (merge 4923e9f), deployed via RMM agent → dashboard v0.2.36, live bundle index-BAUBO9gN.js.

2) **BUG-015 / SPEC-011 (agent missing from Programs and Features):** installer-only fix per spec — added 6 ARP properties + `<Icon>` + AgentIcon component + pinned AgentBinary GUID to installer/gururmm-agent.wxs, and generated installer/gururmm-agent.ico (multi-res 16/32/48/256, 8802 bytes, via Pillow). Reviewed APPROVE WITH NITS (key: .ico must be committed with .wxs or Pluto build fails — it was). Shipped as PR #35 (merge 4bca9f3, .ico in commit 07d14bd). Root submodule pointer bumped (424d610).

Both merged cleanly; Howard explicitly directed fixing BUG-015 despite the usual defer-agent/installer-to-Mike rule.

### Problems Encountered
- **Build-pipeline gate gap:** BUG-015's WiX change merged but the Windows MSI did NOT rebuild — build-windows.sh's change-gate (line ~31) only diffs `agent/`, so installer-only changes log "No agent changes — Windows build skipped" (last-built-commit-windows stuck at 9b34393). Forced a clean one-off build by emptying /opt/gururmm/last-built-commit-windows (the skip condition requires `-n "$LAST_SHA"`), backed up first. Ran build-windows.sh as root (matches webhook context; root holds the Pluto SSH key). Build completed in 623s, signed, marked stable. Routed the durable gate fix to Mike (todo a47884d7 + coord message) since the pipeline is his domain.
- Initial detached-launch RMM command timed out because the backgrounded wrapper held the agent's stdout pipe; verified the build was actually running (setsid inner-redirect worked) and watched it via a background poller instead.

### Configuration Changes
- gururmm: dashboard/src/components/LogAnalysis.tsx (PR #34); installer/gururmm-agent.wxs + new installer/gururmm-agent.ico (PR #35).
- Production: dashboard rebuilt+rsynced (v0.2.36, index-BAUBO9gN.js); Windows agent base MSI rebuilt on Pluto with ARP metadata.
- Root submodule pointer bumps: 424d610 (both PRs).

### Commands & Outputs
- Forced Windows build: emptied last-built-commit-windows → `build-windows.sh` → "Windows build complete: v0.6.50 in 623s", gururmm-agent-base-0.6.50.msi marked stable.
- MSI verification (msiinfo Property table): ARPPRODUCTICON=GuruRMMIcon.ico, ARPURLINFOABOUT=https://rmm.azcomputerguru.com, ARPHELPLINK=https://azcomputerguru.com, ARPCONTACT=Arizona Computer Guru LLC, ARPNOREPAIR=1, ARPNOMODIFY=1.
- MSI: /var/www/gururmm/downloads/gururmm-agent-base-0.6.50.msi (2,654,208 bytes, built 04:03 UTC). last-built-commit-windows now 4bca9f3.

### Infrastructure & Servers
- PLUTO Windows build host: agent id 07a11ece-c744-4cf5-a845-2c5366e36471; build SSH from .30 as root using /root/.ssh/id_ed25519, pinned via /opt/gururmm/pluto_known_hosts.
- gururmm-webhook runs as root, invokes `sudo /opt/gururmm/build-windows.sh`.

### Pending / Incomplete Tasks
- **BUG-015 real-world check:** install gururmm-agent-base-0.6.50.msi on a Win10/11 box, confirm it appears in Programs and Features with icon + uninstall works. Needs a clean Windows test machine (existing agents are site-configured; base MSI has no site key).
- **Mike (todo a47884d7):** extend the build change-gate to watch installer/ (and reconcile stale scripts/ repo copies vs /opt/gururmm).
- **Optional:** flip FEATURE_ROADMAP.md BUG-015 status Open→Fixed (doc PR).

### Reference Information
- PRs: #34 (a11y, merge 4923e9f), #35 (BUG-015, merge 4bca9f3, .ico commit 07d14bd). Root bump 424d610. gururmm main tip 4bca9f3.
- Coord: dashboard component v0.2.36; todo 15a5440f done; todo a47884d7 (gate fix, mike). PLUTO agent 07a11ece.

---

## Update: 22:30 MST — BUG-015 validated on real Win11 + DisplayIcon follow-up shipped

### User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech

### Session Summary
Tested the BUG-015 ARP MSI on Howard's RMM-TEST-MACHINE (Win11 Enterprise Eval 22621, agent 99d6d692) via the gururmm RMM agent. Recon first confirmed the bug: agent 0.6.50 running (GuruRMMAgent + GuruRMMWatchdog) but NO ARP/Uninstall entry. Installed the PR #35 base MSI via a decoupled SYSTEM scheduled task (so the agent's own service restart during install wouldn't kill the command; results written to a temp file, then polled). Result: msiexec exit 0, agent now appears in Programs and Features with DisplayName/Publisher/version/URLs/NoModify/NoRepair, uninstallable, agent stayed enrolled.

One gap found: DisplayIcon registry value was EMPTY — ARPPRODUCTICON cached the icon (C:\Windows\Installer\{ProductCode}\GuruRMMIcon.ico) and the .ico installed to Program Files\GuruRMM\, but Windows Installer didn't write DisplayIcon, so no branded icon rendered. Howard chose to fix it.

Follow-up fix (PR #36): added an ArpDisplayIcon component to installer/gururmm-agent.wxs writing DisplayIcon=[#GuruRMMIconFile] under HKLM Uninstall\[ProductCode] (Bitness always64 to match the 64-bit ARP key). Code Review APPROVE WITH NITS (strong sequencing analysis: RegisterProduct doesn't author DisplayIcon in this config so won't clobber ours; applied nit #2 - used the build-time-checked [#GuruRMMIconFile] File reference instead of a literal path; kept .ico target over the exe so the BRANDED icon shows). Merged PR #36 (33d97e1), root pointer bump 22b221c.

Force-rebuilt the MSI on Pluto again (gate skips installer-only changes; emptied last-built-commit-windows, ran build-windows.sh as root; completed v0.6.50 in 618s). msiinfo confirmed the new MSI's Registry table contains the DisplayIcon row (component ArpDisplayIcon). Re-tested on RMM-TEST-MACHINE (uninstall old -> install new -> verify, one decoupled task): msiexec exit 0, DisplayIcon now = C:\Program Files\GuruRMM\gururmm-agent.ico (file exists), agent enrolled. BUG-015 fully validated end-to-end. Cleaned up all test artifacts (scheduled tasks + temp files) on the machine.

Also answered a Howard question mid-stream (not a bug): offline agents showing 0.6.50 - confirmed 0.6.50 has been the agent version since 2026-05-29 (commit 9b34393), today's work never bumped the agent version, and the dashboard correctly shows per-agent last-reported versions (fleet spans 0.6.2 -> 0.6.50, not all forced to latest).

### Key Decisions
- Tested via a decoupled SYSTEM scheduled task writing to a result file, because the install restarts GuruRMMAgent (the very agent executing the command); the file survives the restart and is polled afterward.
- Uninstall-old-then-install-new for the DisplayIcon retest (same version 0.6.50, new ProductCode) to avoid MajorUpgrade same-version ambiguity and get a definitive clean install.
- DisplayIcon points at the installed .ico (not the exe) to render the branded icon; used [#FileId] reference for build-time link checking.

### Configuration Changes
- gururmm: installer/gururmm-agent.wxs +ArpDisplayIcon component (PR #36, merge 33d97e1). Root submodule pointer bump 22b221c.
- Pluto: rebuilt gururmm-agent-base-0.6.50.msi (now includes DisplayIcon reg write), deployed to /var/www/gururmm/downloads/, marked stable. last-built-commit-windows -> 33d97e1.
- RMM-TEST-MACHINE: agent reinstalled from the fixed MSI (test); all temp/test artifacts removed afterward.

### Commands & Outputs
- DisplayIcon retest (RMM-TEST-MACHINE, agent 99d6d692): msiexec_exit=0; DisplayIcon=C:\Program Files\GuruRMM\gururmm-agent.ico (file exists); Publisher=Arizona Computer Guru LLC; agent_service=Running. New ProductCode {A9CAA6D5-FCAA-4FFA-87AC-143E8BB455DC}.
- New MSI Registry table row: ArpDisplayIcon -> Uninstall\[ProductCode]\DisplayIcon = [#GuruRMMIconFile].
- Force build: v0.6.50 in 618s.

### Infrastructure & Servers
- RMM-TEST-MACHINE: Win11 Enterprise Eval build 22621, GuruRMM agent id 99d6d692-99e0-4359-9f9c-f43be89f49e5 (a stale 0.6.38 ghost enrollment 7d3456f5 also exists for the same box).

### Pending / Incomplete Tasks
- Mike (todo a47884d7): extend the build change-gate to watch installer/ so installer-only changes rebuild the MSI without a manual force-build.
- Optional: flip FEATURE_ROADMAP.md BUG-015 status Open -> Fixed (doc PR).

### Reference Information
- PR #36 (DisplayIcon): merge 33d97e1, source commit 9a2245b; root bump 22b221c. gururmm main tip 33d97e1.
- #dev-alerts: build + DisplayIcon-verified alerts posted. Mike messaged (BUG-015 fully done).
