sync: auto-sync from HOWARD-HOME at 2026-06-21 20:36:14
Author: Howard Enos Machine: HOWARD-HOME Timestamp: 2026-06-21 20:36:14
This commit is contained in:
109
.claude/skills/screenconnect/SKILL.md
Normal file
109
.claude/skills/screenconnect/SKILL.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
name: screenconnect
|
||||
description: >-
|
||||
Manage the ACG ConnectWise ScreenConnect (Control) instance via the RESTful API
|
||||
Manager extension: list/inspect sessions, build PARAMETERIZED access installers
|
||||
(self-tag a device into the right Company/Site/Tag), run backstage commands,
|
||||
message guests, and update custom properties. Read-only by default; state-
|
||||
changing ops are gated behind --confirm. Pairs with /rmm to push the installer.
|
||||
Triggers: screenconnect, connectwise control, sc session, remote access agent,
|
||||
build screenconnect installer, run command on a screenconnect session, tag a
|
||||
device in screenconnect, deploy screenconnect via rmm.
|
||||
---
|
||||
|
||||
# ScreenConnect (ConnectWise Control) Skill
|
||||
|
||||
Standalone CLI for the ACG ScreenConnect cloud instance
|
||||
(`https://computerguru.screenconnect.com`) via the **RESTful API Manager**
|
||||
extension. Read-only by default; writes gated behind `--confirm`.
|
||||
|
||||
## Running the CLI
|
||||
|
||||
```bash
|
||||
SC="bash $CLAUDETOOLS_ROOT/.claude/scripts/py.sh C:/claudetools/.claude/skills/screenconnect/scripts/sc.py"
|
||||
$SC status # auth check + instance info
|
||||
$SC sessions --name "<hostname>" # find sessions by Name
|
||||
$SC session <sessionID> # full session detail
|
||||
$SC build-installer --platform msi --name HOST --company "X" --site "Y" --tag "Z"
|
||||
$SC send-command --session <id> --command "..." --confirm
|
||||
$SC set-properties --session <id> --props-json '["Company","Site","Tag"]' --confirm
|
||||
$SC raw --method GetSessionsByName --body '{"sessionName":""}'
|
||||
```
|
||||
|
||||
Transport auto-selects httpx, else stdlib urllib (no hard dependency).
|
||||
|
||||
## Credentials & auth (VERIFIED)
|
||||
|
||||
API secret is NEVER hardcoded - loaded from the SOPS vault
|
||||
`msp-tools/screenconnect.sops.yaml` field `credentials.api_secret` (or the
|
||||
`SCREENCONNECT_API_SECRET` env override). Auth is two headers:
|
||||
|
||||
```
|
||||
CTRLAuthHeader: <raw api_secret> (NO "Basic" prefix - Basic auth 401s)
|
||||
Origin: https://computerguru.screenconnect.com
|
||||
```
|
||||
|
||||
Endpoints: `POST <base>/App_Extensions/2d558935-686a-4bd0-9991-07539f5fe749/Service.ashx/<Method>`.
|
||||
Reads take a JSON object; the write methods take a **positional array**.
|
||||
Custom properties on this instance: **CP1=Company, CP2=Site, CP3=Tag** (up to CP8).
|
||||
|
||||
## The deploy workflow (RMM push -> self-tag -> control)
|
||||
|
||||
This is the headline use case - set a device for SC and have it land correctly:
|
||||
|
||||
1. `build-installer` with the device's Company/Site/Tag (+ name) -> a parameterized
|
||||
access-installer URL (`?e=Access&y=Guest&t=<name>&c=<Company>&c=<Site>&c=<Tag>`).
|
||||
The cloud serves a pre-keyed installer; the `c=` params self-tag the agent.
|
||||
2. Push that installer via `/rmm` (download + `msiexec /i ... /qn` as SYSTEM).
|
||||
3. The SC agent connects -> a session named `<name>` appears with CP1/CP2/CP3 set.
|
||||
4. `sessions --name <name>` -> get the sessionID -> control it (`send-command`, etc.).
|
||||
|
||||
VERIFIED end-to-end on RMM-TEST-MACHINE 2026-06-22 (installed, self-tagged
|
||||
Company/Site/Tag, ran a command, re-tagged via set-properties).
|
||||
|
||||
## Method surface (probed live 2026-06-22)
|
||||
|
||||
**Available (CLI-exposed):**
|
||||
- Reads: `GetSessionsByName` (matches the Name field; "" -> blank-name sessions),
|
||||
`GetSessionDetailsBySessionID`, `GetSessionBySessionID`.
|
||||
- Writes (gated): `SendCommandToSession` `[sessionID, command]`,
|
||||
`SendMessageToSession` `[sessionID, message]`,
|
||||
`UpdateSessionCustomProperties` `[sessionID, [cp1,cp2,cp3,...]]`,
|
||||
`CreateSession` (array; not the primary path - the installer creates access sessions).
|
||||
|
||||
**MISSING on this extension version (NOT a deploy/control blocker):**
|
||||
- `GetSessions` / `GetAllSessions` / `GetSessionGroups` (full-fleet inventory) ->
|
||||
"web method does not exist". Listing ALL agents needs Mike to update the RESTful
|
||||
API Manager extension to expose a list-all method. Until then, find sessions by
|
||||
Name (the installer sets the Name = machine name, so by-name lookup works).
|
||||
|
||||
## Safety gating
|
||||
|
||||
`send-command`, `send-message`, `set-properties` refuse to run without `--confirm`
|
||||
(print what they WOULD do, exit 3). `raw` also refuses state-changing method names
|
||||
(sendcommand/updatesession/create/delete/end/transfer/install/...) without
|
||||
`--confirm`. NEVER run `send-command` against a production client session casually -
|
||||
it executes on the guest. Test against a known test machine.
|
||||
|
||||
## Error logging
|
||||
|
||||
On a GENUINE functional error (auth failure, unexpected API response, transport
|
||||
failure) the CLI logs it to `errorlog.md` via `log-skill-error.sh` before
|
||||
surfacing it. It does NOT log expected/handled conditions - a missing extension
|
||||
method ("web method does not exist"), a rate-limit (429), the `raw` probe path, or
|
||||
selftest runs (`SC_SUPPRESS_ERRORLOG=1`) are skipped (see `_should_log_error`).
|
||||
|
||||
## Future: GuruRMM integration (Raw idea)
|
||||
|
||||
Tie this into GuruRMM (parallels the multi-vendor security thought, Feature 6):
|
||||
when a device is flagged for ScreenConnect in the RMM, GuruRMM looks up the
|
||||
device's client/site/tags and calls `build-installer` to produce a parameterized
|
||||
installer, then pushes it via the agent - so the device installs the correct SC
|
||||
client and self-places into the right Company/Site/Tag automatically. `set-
|
||||
properties` keeps the SC tags in sync when the RMM record changes. Capture/advance
|
||||
via RMM_THOUGHTS -> /shape-spec (needs Mike's go).
|
||||
|
||||
## Reference
|
||||
|
||||
Verified method/param spec, auth, installer params, and the GetSessions gap:
|
||||
`references/api-reference.md`.
|
||||
63
.claude/skills/screenconnect/references/api-reference.md
Normal file
63
.claude/skills/screenconnect/references/api-reference.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# ScreenConnect (ConnectWise Control) API Reference - ACG instance
|
||||
|
||||
Live-verified spec for the ACG ScreenConnect cloud instance via the RESTful API
|
||||
Manager extension. Source: ConnectWise ScreenConnect docs
|
||||
(https://docs.connectwise.com/ScreenConnect_Documentation/Developers) + live
|
||||
probing 2026-06-22.
|
||||
|
||||
## Connection & auth
|
||||
|
||||
- **Instance:** `https://computerguru.screenconnect.com`
|
||||
- **Extension GUID:** `2d558935-686a-4bd0-9991-07539f5fe749` (RESTful API Manager)
|
||||
- **Endpoint:** `POST <instance>/App_Extensions/<guid>/Service.ashx/<Method>`
|
||||
- **Auth headers (VERIFIED):**
|
||||
- `CTRLAuthHeader: <raw api_secret>` (NO "Basic" prefix - Basic auth 401s)
|
||||
- `Origin: https://computerguru.screenconnect.com`
|
||||
- `Content-Type: application/json`
|
||||
- **Secret:** SOPS vault `msp-tools/screenconnect.sops.yaml` field `credentials.api_secret`.
|
||||
- **Param convention:** reads take a JSON object; write methods take a POSITIONAL ARRAY.
|
||||
- **Custom properties (this instance):** CP1=Company, CP2=Site, CP3=Tag (up to CP8).
|
||||
|
||||
## Methods (probed live 2026-06-22)
|
||||
|
||||
| Method | Params | Status | Notes |
|
||||
|---|---|---|---|
|
||||
| `GetSessionsByName` | `{"sessionName":"<name>"}` | VERIFIED | Matches the session Name field. "" returns blank-Name (unattended) sessions. CLI `sessions`. |
|
||||
| `GetSessionDetailsBySessionID` | `{"sessionID":"<id>"}` | VERIFIED | Full session object (CustomPropertyValues, ActiveConnections, ...). CLI `session`. |
|
||||
| `GetSessionBySessionID` | `{"sessionID":"<id>"}` | VERIFIED | Returns the session (or [] if none). |
|
||||
| `SendCommandToSession` | `["<sessionID>","<command>"]` | VERIFIED (gated) | Runs a backstage command on the guest. CLI `send-command`. STATE-CHANGING. |
|
||||
| `SendMessageToSession` | `["<sessionID>","<message>"]` | wired (array) | Chat message to the guest. CLI `send-message`. STATE-CHANGING. |
|
||||
| `UpdateSessionCustomProperties` | `["<sessionID>",["cp1","cp2","cp3",...]]` | VERIFIED (gated) | Set CP1=Company/CP2=Site/CP3=Tag. CLI `set-properties`. STATE-CHANGING. |
|
||||
| `CreateSession` | array (signature TBD) | EXISTS | Not the primary path - the installer creates access sessions. `raw` only. |
|
||||
| `GetSessions` / `GetAllSessions` / `GetSessionGroups` | - | MISSING | "web method does not exist". Full-fleet inventory needs an extension update (Mike). |
|
||||
|
||||
## Parameterized access installer (the deploy capability)
|
||||
|
||||
The cloud serves a pre-keyed installer (no thumbprint param needed - the relay is
|
||||
the instance subdomain). Build URL:
|
||||
|
||||
```
|
||||
<instance>/Bin/ScreenConnect.ClientSetup.<ext>?e=Access&y=Guest&t=<name>&c=<CP1>&c=<CP2>&c=<CP3>&c=&c=&c=&c=&c=
|
||||
```
|
||||
|
||||
- `<ext>`: msi | exe (Windows), pkg (macOS), deb | rpm | sh (Linux). VERIFIED: the
|
||||
`.msi` returns HTTP 200 / application/x-msi (~16 MB).
|
||||
- `e=Access` (unattended access agent), `y=Guest` (the guest/installed role).
|
||||
- `t=<name>`: the session Name (set it = machine name so by-name lookup works).
|
||||
- repeated `c=`: custom properties in order (CP1=Company, CP2=Site, CP3=Tag, ... 8 slots).
|
||||
- Windows silent install: `msiexec /i <file> /qn /norestart`.
|
||||
|
||||
CLI `build-installer --platform msi --name HOST --company X --site Y --tag Z`.
|
||||
|
||||
VERIFIED end-to-end 2026-06-22: RMM-pushed parameterized .msi -> session
|
||||
`RMM-TEST-MACHINE` appeared with CustomPropertyValues `["AZ Computer Guru","Howard-VM","SC-TEST",...]`;
|
||||
`send-command` created a marker file on the guest; `set-properties` re-tagged CP3.
|
||||
|
||||
## Error handling
|
||||
|
||||
- Missing method -> HTTP 500 body `"Web method does not exist"` (treated as expected,
|
||||
not a skill failure - not logged).
|
||||
- Bad params on a real method -> HTTP 500 `"session manager fault"` /
|
||||
NullReferenceException (a bogus sessionID faults the same as a bad shape, so the
|
||||
generic fault does NOT distinguish - verify writes against a known test session).
|
||||
- A bare `{}` to a write method faults: writes need the positional array.
|
||||
@@ -16,12 +16,13 @@ application/json and the body is the method's parameters (object or array).
|
||||
Credentials: never hardcoded. api_secret loaded at runtime from the SOPS vault,
|
||||
or the SCREENCONNECT_API_SECRET env var (testing override).
|
||||
|
||||
NOTE (instance state, 2026-06-21): the installed RESTful API Manager extension is
|
||||
LIMITED — only `GetSessionsByName` exists; other methods 500 "Web method does not
|
||||
exist". Full control (SendCommandToSession, GetSessions, UpdateSessionCustom-
|
||||
Properties, ...) requires updating the extension on the instance. The client is
|
||||
built to expose those methods as soon as the extension is unlocked; `raw()` probes
|
||||
arbitrary methods in the meantime.
|
||||
INSTANCE METHOD SURFACE (probed live 2026-06-22): control IS available -
|
||||
GetSessionsByName, GetSessionDetailsBySessionID, GetSessionBySessionID (reads,
|
||||
JSON-object params); SendCommandToSession, SendMessageToSession,
|
||||
UpdateSessionCustomProperties, CreateSession (writes, POSITIONAL-ARRAY params).
|
||||
MISSING on this extension version: GetSessions/GetAllSessions/GetSessionGroups
|
||||
(full-fleet inventory) - "web method does not exist"; pending an admin update of
|
||||
the RESTful API Manager extension. `raw()` probes arbitrary methods.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -205,23 +206,24 @@ class ScreenConnectClient:
|
||||
return self.call("GetSessionsByName", {"sessionName": session_name})
|
||||
|
||||
# ======================================================================
|
||||
# Methods pending the extension unlock (currently 500 "web method does not
|
||||
# exist"). Exposed here so the CLI is ready; verify each once unlocked.
|
||||
# Shapes are best-effort from the RESTful API Manager docs and MUST be
|
||||
# confirmed by live probing before relying on them.
|
||||
# Control + detail methods (all VERIFIED LIVE 2026-06-22 on the ACG instance).
|
||||
# Reads take a JSON object {sessionID:...}; the POST/write methods take a
|
||||
# POSITIONAL ARRAY. NOTE: full-fleet inventory (GetSessions) is NOT exposed by
|
||||
# this extension version (returns "web method does not exist") - pending an
|
||||
# admin update of the RESTful API Manager extension (see SKILL.md).
|
||||
# ======================================================================
|
||||
def get_session_details(self, session_id: str) -> Any:
|
||||
"""GetSessionDetailsBySessionID — full detail for one session. PENDING UNLOCK."""
|
||||
"""GetSessionDetailsBySessionID - full detail for one session. VERIFIED."""
|
||||
return self.call("GetSessionDetailsBySessionID", {"sessionID": session_id})
|
||||
|
||||
def send_command_to_session(self, session_id: str, command: str) -> Any:
|
||||
"""SendCommandToSession — run a backstage command on a guest. EXISTS on this
|
||||
"""SendCommandToSession - run a backstage command on a guest. EXISTS on this
|
||||
instance. POST body is a POSITIONAL ARRAY [sessionID, command]
|
||||
(e.g. ["<uuid>", "ipconfig"]). STATE-CHANGING (gate behind --confirm)."""
|
||||
return self.call("SendCommandToSession", [session_id, command])
|
||||
|
||||
def send_message_to_session(self, session_id: str, message: str) -> Any:
|
||||
"""SendMessageToSession — send a chat message to a guest. EXISTS.
|
||||
"""SendMessageToSession - send a chat message to a guest. EXISTS.
|
||||
POST body positional array [sessionID, message]. STATE-CHANGING."""
|
||||
return self.call("SendMessageToSession", [session_id, message])
|
||||
|
||||
@@ -236,7 +238,7 @@ class ScreenConnectClient:
|
||||
return self.call(method, body, http_method=http_method)
|
||||
|
||||
# ======================================================================
|
||||
# INSTALLER BUILDER (no API call — constructs the parameterized access
|
||||
# INSTALLER BUILDER (no API call - constructs the parameterized access
|
||||
# installer URL so a device self-tags into the right Company/Site/Tag).
|
||||
# ======================================================================
|
||||
def build_installer_url(
|
||||
|
||||
80
.claude/skills/screenconnect/scripts/selftest.py
Normal file
80
.claude/skills/screenconnect/scripts/selftest.py
Normal file
@@ -0,0 +1,80 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read-only / gating self-test for the screenconnect skill.
|
||||
|
||||
Runs each CLI command as a subprocess and checks exit code + output markers. NO
|
||||
state-changing API calls (writes are tested only in their --confirm-absent refusal
|
||||
path). build-installer is a pure URL build (no network). Prints a PASS/FAIL report.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
SC = os.path.join(HERE, "sc.py")
|
||||
results = []
|
||||
|
||||
|
||||
def run(args):
|
||||
env = dict(os.environ)
|
||||
env.setdefault("CLAUDETOOLS_ROOT", "C:/claudetools")
|
||||
env["PYTHONIOENCODING"] = "utf-8"
|
||||
env["SC_SUPPRESS_ERRORLOG"] = "1" # never let the self-test touch errorlog.md
|
||||
p = subprocess.run([sys.executable, SC] + args, capture_output=True,
|
||||
text=True, env=env, timeout=120)
|
||||
return p.returncode, p.stdout, p.stderr
|
||||
|
||||
|
||||
def check(name, args, *, want_rc=None, out_has=None, out_json_ok=False):
|
||||
rc, out, err = run(args)
|
||||
problems = []
|
||||
if want_rc is not None and rc != want_rc:
|
||||
problems.append(f"rc={rc} want {want_rc}")
|
||||
if out_has and out_has not in out:
|
||||
problems.append(f"stdout missing {out_has!r}")
|
||||
if out_json_ok:
|
||||
try:
|
||||
json.loads(out)
|
||||
except Exception as e:
|
||||
problems.append(f"stdout not JSON: {e}")
|
||||
results.append(("PASS" if not problems else "FAIL", name, "; ".join(problems)))
|
||||
|
||||
|
||||
# --- reads: succeed (rc 0) [hit the live instance] ---
|
||||
check("status", ["status"], want_rc=0, out_has="Authenticated")
|
||||
check("sessions", ["sessions"], want_rc=0, out_has="Sessions:")
|
||||
check("sessions json", ["sessions", "--json"], want_rc=0, out_json_ok=True)
|
||||
|
||||
# --- build-installer: pure URL build (no network) ---
|
||||
check("build-installer", ["build-installer", "--name", "HOST", "--company", "AZ Computer Guru",
|
||||
"--site", "Howard-VM", "--tag", "T1"], want_rc=0, out_has="e=Access")
|
||||
check("build-installer encodes spaces", ["build-installer", "--company", "A B C"],
|
||||
want_rc=0, out_has="A%20B%20C")
|
||||
check("build-installer json", ["build-installer", "--json", "--tag", "X"],
|
||||
want_rc=0, out_json_ok=True)
|
||||
|
||||
# --- gating: writes refuse without --confirm (rc 3, no API call) ---
|
||||
check("send-command no confirm -> rc3", ["send-command", "--session", "x", "--command", "whoami"],
|
||||
want_rc=3, out_has="Would")
|
||||
check("send-message no confirm -> rc3", ["send-message", "--session", "x", "--message", "hi"],
|
||||
want_rc=3)
|
||||
check("set-properties no confirm -> rc3",
|
||||
["set-properties", "--session", "x", "--props-json", '["A","B","C"]'], want_rc=3)
|
||||
check("set-properties bad json -> rc2",
|
||||
["set-properties", "--session", "x", "--props-json", "{bad", "--confirm"], want_rc=2)
|
||||
|
||||
# --- raw: read ok; destructive method refused without --confirm ---
|
||||
check("raw read ok", ["raw", "--method", "GetSessionsByName", "--body", '{"sessionName":""}'],
|
||||
want_rc=0)
|
||||
check("raw SendCommandToSession no confirm -> rc3",
|
||||
["raw", "--method", "SendCommandToSession", "--body", "[]"], want_rc=3)
|
||||
|
||||
# --- report ---
|
||||
print("\n==== screenconnect skill self-test ====")
|
||||
npass = sum(1 for r in results if r[0] == "PASS")
|
||||
for status, name, prob in results:
|
||||
print(f"[{status}] {name}" + (f" -> {prob}" if prob else ""))
|
||||
print(f"\n{npass}/{len(results)} passed, {len(results)-npass} failed")
|
||||
sys.exit(0 if npass == len(results) else 1)
|
||||
@@ -17,6 +17,8 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
|
||||
|
||||
<!-- Append entries below this line -->
|
||||
|
||||
2026-06-22 | Howard-Home | build/pipeline-status | [friction] reported BUG-021 Windows build as still-failing from a build-log snapshot; it had already been fixed (1dce66d) + gone green (v0.6.67) by report time. Re-check the LIVE last-built-commit marker vs origin/main (and the most recent build SUCCESS line, not just the last FAILED line) before asserting build status or escalating a build bug. [ctx: ref=stale-audit-base-friction proj=guru-rmm]
|
||||
|
||||
2026-06-22 | Howard-Home | guruscan/GuruScan.psm1 | HitmanPro exit-code misparse: real HitmanPro returns bitmask (exit 5 = 36 threats quarantined + reboot required) but code mapped only {1,2}; reported total_threats=0 reboot_required=False on a real 36-threat removal, so reboot-cleanup lifecycle never fired. Fixed: bit0=threats, bit4=reboot [ctx: host=DESKTOP-MS42HNC engine=HitmanPro-3.8.50]
|
||||
|
||||
2026-06-22 | Howard-Home | screenconnect/browser-automation | [friction] HOWARD-HOME: ff.py Firefox daemon won't launch (port 9333 dead, silent 60s timeout) AND cdp.py ModuleNotFoundError 'websocket' (websocket-client not installed) -> can't drive the SC website to build the access installer. Fix: pip install websocket-client; verify playwright firefox installed for ff.py. [ctx: machine=HOWARD-HOME tool=ff.py,cdp.py]
|
||||
|
||||
@@ -119,3 +119,82 @@ Reworked BUG-018 (PR #41) to the 202+bg design, renumbered SPEC-021's migration
|
||||
- Branch tips: BUG-018 `de9b089`, SPEC-021 `9171f84`. BUG-019 built `v0.6.67`, marker `8b5e0dc`.
|
||||
- New from Mike (via sync): `gururmm-build` skill, guru-rmm `docs/BUILD.md`, memory `feedback_gururmm_build_verification`; fabb3421 app DELETED (use Exchange Operator b43e7342 for client mail, 1873b1b0 for /mailbox).
|
||||
- Coord msgs to GURU-5070: 7a4747b8 (claim), 0c2ae45e (done + SSH flag). Discord DM 1518411100927033464 (build-outage flag).
|
||||
|
||||
---
|
||||
|
||||
## Update: 20:35 PT — Roadmap verification, BUG-021 correction, BUG-022 fix (watchdog dead code)
|
||||
|
||||
### Session Summary
|
||||
Continued the roadmap verification pass after compaction. Did a deep functional verification of
|
||||
the live GuruRMM system (270 agents / 178 online, metrics flowing ~2531 rows/15 min, alerts with 0
|
||||
legacy null dedup_keys, per-agent API endpoints all 200 on a real agent). The one unverified spot
|
||||
was `watchdog_events`/`watchdog_alerts` = 0 all-time. Traced both paths end-to-end against
|
||||
`origin/main`: the watchdog's REST escalation path (`watchdog-alert`, fires after 3 failed restarts)
|
||||
is sound — 0 alerts = healthy fleet. But `watchdog_events` = 0 because the agent **never produces**
|
||||
a `WatchdogEvent`: the WS variant was defined + fully server-handled (`insert_watchdog_event` + a
|
||||
`watchdog_events` table) yet had no producer, and architecturally couldn't (the watchdog is a
|
||||
separate companion process with no WS connection). Dead/orphaned path → filed BUG-022 (LOW).
|
||||
|
||||
Mid-session, Howard noted Mike likely fixed the earlier build issues. Re-checked the LIVE build
|
||||
marker and found I had been reporting a stale snapshot: the Windows build went **green** at
|
||||
2026-06-22 02:19 (`v0.6.67`, `last-built-commit-windows == origin/main 1dce66d`). BUG-021 was
|
||||
already **fixed** on main via `1dce66d` (pinned `getrandom 0.3.1` + `zeroize 1.8.1` below
|
||||
edition2024 — exactly the dep-pin I had diagnosed + flagged; the fix even reused the BUG-021 label).
|
||||
Also on main since the snapshot: BUG-018 (202+bg, `cea87d4`) and the Event Log Watch management UI
|
||||
(`0fa65f5`). Corrected the BUG-021 roadmap entry to **Fixed** (`97045ec`).
|
||||
|
||||
Then fixed BUG-022. For a LOW dead-code defect, chose option (a) — **remove the orphaned path** —
|
||||
over building a new feature (granular watchdog reporting is a product call for Mike). Removed the
|
||||
`WatchdogEvent` variant + payload + enum from `agent/src/transport/mod.rs`, the variant + payload
|
||||
struct + match arm + `insert_watchdog_event` call from `server/src/ws/mod.rs`, and the
|
||||
`watchdog_events::*` re-export from `server/src/db/mod.rs`; gutted `db/watchdog_events.rs` to a
|
||||
doc-only stub (keeps the table↔module invariant; left the empty table to avoid a migration-number
|
||||
collision with the open PR train). Compile-verified on the build server: `cargo check` clean on
|
||||
server (48.9s) + agent (15.7s), no new warnings. Pushed `fix/bug-022-watchdog-event-deadcode`
|
||||
(`4eb5054`), opened **PR #45** (code) and **PR #46** (docs: BUG-021/BUG-022 fixed + an RMM thought
|
||||
for the REST-based granular-watchdog follow-up). Did not merge — merging the code PR triggers a
|
||||
fleet build+deploy, left to Howard/Mike.
|
||||
|
||||
### Key Decisions
|
||||
- BUG-022 fix = **remove dead code**, not implement granular events. Proportionate for LOW; the
|
||||
feature alternative needs Mike's go (GuruRMM project) and a design (REST producer, since the
|
||||
watchdog has no WS connection). Captured the feature idea in RMM_THOUGHTS instead of discarding it.
|
||||
- Left the empty `watchdog_events` table in place (no DROP migration) to avoid colliding with the
|
||||
in-flight PR migration numbers (061/062/063 on open PRs #40-42); flagged for a future consolidated
|
||||
cleanup migration.
|
||||
- Kept the code fix branch **code-only** and put the doc status flip on the existing docs branch
|
||||
(disjoint files → no merge conflict between PR #45 and #46).
|
||||
- Did not merge either PR — merging code to main = fleet deploy (hard-to-reverse, outward-facing).
|
||||
|
||||
### Problems Encountered
|
||||
- **Stale build-status reporting (friction, logged):** reported BUG-021 as still-failing from a
|
||||
build-log snapshot; it had already gone green by report time. Same "acted on a point-in-time read"
|
||||
class as the earlier stale-audit-base slip. Fix recorded: re-check the LIVE `last-built-commit`
|
||||
marker vs `origin/main` (and the latest build SUCCESS line, not just the last FAILED line) before
|
||||
asserting build status. `errorlog.md` ref=`stale-audit-base-friction`.
|
||||
- **Submodule reset to stale `2e469f1` again:** the initial watchdog grep ran against old code.
|
||||
Re-ran authoritatively with `git grep origin/main` and did all edits in worktrees off `origin/main`
|
||||
+ push-by-SHA (the established concurrency-safe pattern).
|
||||
- **`cargo: command not found` on non-interactive SSH:** the build server's cargo is under
|
||||
`~/.cargo/bin`; sourcing `~/.cargo/env` fixed it (cargo 1.96 on `.30`).
|
||||
|
||||
### Configuration Changes (this update)
|
||||
- guru-rmm `fix/bug-022-watchdog-event-deadcode` (`4eb5054`): removed dead WatchdogEvent path across
|
||||
`agent/src/transport/mod.rs`, `server/src/ws/mod.rs`, `server/src/db/mod.rs`,
|
||||
`server/src/db/watchdog_events.rs` (doc-only stub).
|
||||
- guru-rmm `docs/bug-021-windows-build` (`487431f`): BUG-021→Fixed + BUG-022 entry→Fixed in
|
||||
`docs/FEATURE_ROADMAP.md`; granular-watchdog-visibility thought appended to `docs/RMM_THOUGHTS.md`.
|
||||
- `errorlog.md`: one `--friction` entry (stale build-status reporting).
|
||||
|
||||
### Pending / Incomplete (this update)
|
||||
- **PR #45** (code) + **PR #46** (docs) await merge by Howard/Mike. Merge #45 before #46. Merging #45
|
||||
= fleet build+deploy.
|
||||
- Empty `watchdog_events` table still present — drop in a future consolidated cleanup migration.
|
||||
- Granular watchdog visibility (REST `watchdog-event` producer) — RMM_THOUGHTS, Raw, needs Mike's go.
|
||||
|
||||
### Reference (this update)
|
||||
- BUG-021 fix on main: `1dce66d` (getrandom 0.3.1 + zeroize 1.8.1 pin). BUG-018 on main: `cea87d4`.
|
||||
Event Log Watch UI: `0fa65f5`. Windows build green: `v0.6.67`, marker `1dce66d`, 2026-06-22 02:19.
|
||||
- Branch tips: fix `4eb5054`, docs `487431f`. PRs: #45 (code), #46 (docs).
|
||||
- Verified live: 270 agents/178 online; REST `watchdog-alert` path sound; `watchdog_events`=0 = dead
|
||||
WS path (no producer).
|
||||
|
||||
Reference in New Issue
Block a user