# Session Log — 2026-05-28 — SPEC-014 Implementation + Cascades Billing

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

---

## Session Summary

Session continued from a compacted context covering SPEC-013/014 spec creation, SPEC-013 deferral per Mike, and the Agents sidebar nav link (Monitor icon, between Clients and Scripts in Layout.tsx). Those items are documented in `2026-05-28-howard-gururmm-agents-nav.md` and `2026-05-28-howard-gururmm-spec-013-014.md`. This session picked up with SPEC-014 implementation and Cascades of Tucson billing.

SPEC-014 (Windows Event Log Viewer) was implemented in full across all three layers. Agent side: added `EventLogWatchRule` and `EventLogMatchEntry` structs to `transport/mod.rs`, added `event_log_watches` field (optional) to `ConfigUpdatePayload`, added `EventLogMatches` variant to `AgentMessage`, and created `agent/src/event_log.rs` with `query_watch_rule()` (builds `Get-WinEvent` PowerShell command, parses JSON output). A background watcher task was added to `websocket.rs` (`#[cfg(target_os = "windows")]`): polls every 60 seconds, maintains per-rule `last_checked` timestamps, sends `EventLogMatches` back to server when rules fire. Server side: migration `047_event_log_watches.sql` creates the `event_log_watches` table; `db/event_log_watches.rs` provides CRUD; `api/event_log_watches.rs` provides REST CRUD and pushes updated rules to the agent via ConfigUpdate after each write; `api/event_log_query.rs` handles on-demand `GET /api/agents/:id/eventlog` by building a PS command, dispatching via existing command infra, polling DB 15s/500ms, parsing stdout JSON; `ws/mod.rs` `AgentMessage::EventLogMatches` handler creates alerts with `dedup_key = "evtlog:{agent_id}:{rule_id}:{event_id}"`. Dashboard: `EventLogViewer.tsx` new component with filter bar (log name, level, event ID, source, time range) and expandable results table with severity color coding; `AgentDetail.tsx` updated to add `"events"` to TabId, Events tab (Windows-only), and TabPanel wiring. Committed as `0253451` and pushed to gururmm.

Howard explicitly delegated backend follow-up items to Mike: policy tab UI for watch rules, and pushing watch rules to the agent on initial connect. These are deferred and noted in pending tasks.

Howard asked about Chris Knight's email/UPN situation at Cascades of Tucson — the existing address `c.knight@cascadestucson.com` needed to become `chris.knight@cascadestucson.com`. Advisory provided: Option 1 was to add the new alias and promote it to primary while leaving the old alias in place (no MFA impact — authenticator binding is tied to Azure AD Object ID, not UPN); Option 2 was the same but also deleting the old alias after the cutover. Howard proceeded with adding the alias, promoting it to primary, then removing the old alias. Confirmed MFA was not affected.

Cascades ticket #32332 (Syncro internal ID 111233015) was billed for 0.5h warranty work for the UPN rename. Line item added (product 1049360, Labor- Warranty work, qty 0.5, $0.00, user_id 1750, taxable: false). Invoice #67670 created ($0.00 — correct for warranty). Ticket status updated to Invoiced. Bot alert posted to #bot-alerts. Note: the Syncro `POST /tickets/{id}/comments` and `POST /tickets/{id}/invoice` sub-resource endpoints both returned 404 — `add_line_item` and `POST /invoices` (top-level with customer_id + ticket_id) were used as working alternatives. This is a pattern change from prior sessions that used sub-resource paths with smaller ticket IDs.

---

## Key Decisions

- **SPEC-014 on-demand query reuses existing PowerShell infra** — rather than a new CommandType, the server builds the Get-WinEvent PS command and dispatches it through the existing command relay path. Phase 1 is pure PowerShell; Phase 2 (native Rust `windows` crate EvtQuery) deferred.
- **Watch rules delivered via ConfigUpdate** — added `event_log_watches: Option<Vec<EventLogWatchRule>>` to the existing `ConfigUpdatePayload` struct. Backward compatible (Option), no new message type needed.
- **Dedup key format** — `evtlog:{agent_id}:{rule_id}:{event_id}` prevents duplicate alerts for the same event firing on multiple poll cycles while still allowing distinct alerts for different EventRecordID values.
- **Events tab Windows-only** — Linux (syslog/journald) and macOS (unified log) are different paths; tab is hidden for non-Windows agents in v1.
- **Howard deferred backend follow-up to Mike** — policy tab for watch rules and agent-connect rule push are noted as Mike's items. Howard does not want to own backend implementation work.
- **Chris Knight UPN rename with alias promotion** — added chris.knight as alias, promoted to primary (updates UPN + sign-in name + primary SMTP), deleted c.knight alias. MFA unaffected because authenticator is bound to Azure AD Object ID.
- **Warranty billing skips comment** — Syncro POST /tickets/{id}/comments returned 404. Per hard rule: stopped rather than experimenting. Comment skipped; line item and invoice created via working endpoints.
- **Invoice via POST /invoices (top-level)** — POST /tickets/{id}/invoice returned 404. Used `POST /api/v1/invoices` with `customer_id` and `ticket_id` in body. Returned valid invoice object.

---

## Problems Encountered

- **Syncro POST /tickets/{id}/comments → 404** — The comments sub-resource endpoint is not responding for ticket IDs in the new large-number format (111233015 vs. prior small IDs like 32313). GET to same ticket ID works. Tried both nested path and top-level `ticket_comments` endpoint — both 404. Per hard rule, stopped and reported. Comment skipped for this ticket.
- **Syncro POST /tickets/{id}/invoice → 404** — Same pattern. Resolved by using `POST /invoices` at top level with `customer_id` and `ticket_id` in payload — succeeded and returned invoice id 1650452791, number 67670.
- **Bot alert Unicode → 400** — First bot alert attempt included `→` (U+2192). Discord returned `{"message": "The request body contains invalid JSON.", "code": 50109}`. Fixed by using ASCII `->` in the message string.

---

## Configuration Changes

- `projects/msp-tools/guru-rmm/agent/src/transport/mod.rs` — added `EventLogWatchRule`, `EventLogMatchEntry` structs; `event_log_watches: Option<Vec<EventLogWatchRule>>` to `ConfigUpdatePayload`; `EventLogMatches` to `AgentMessage`
- `projects/msp-tools/guru-rmm/agent/src/event_log.rs` — new module; `query_watch_rule()` Get-WinEvent PS builder + JSON parser; non-Windows stub
- `projects/msp-tools/guru-rmm/agent/src/transport/websocket.rs` — added `#[cfg(target_os = "windows")]` 60s background watcher task
- `projects/msp-tools/guru-rmm/server/migrations/047_event_log_watches.sql` — new table `event_log_watches`
- `projects/msp-tools/guru-rmm/server/src/db/event_log_watches.rs` — new; `EventLogWatch` model, CRUD
- `projects/msp-tools/guru-rmm/server/src/api/event_log_watches.rs` — new; REST CRUD + ConfigUpdate push on write
- `projects/msp-tools/guru-rmm/server/src/api/event_log_query.rs` — new; `GET /api/agents/:id/eventlog` on-demand handler
- `projects/msp-tools/guru-rmm/server/src/ws/mod.rs` — `EventLogMatches` handler: dedup-keyed alert creation
- `projects/msp-tools/guru-rmm/server/src/api/mod.rs` — added `pub mod event_log_query; pub mod event_log_watches;` and 4 new routes
- `projects/msp-tools/guru-rmm/dashboard/src/components/EventLogViewer.tsx` — new component
- `projects/msp-tools/guru-rmm/dashboard/src/pages/AgentDetail.tsx` — added `"events"` TabId, Windows-only Events tab, TabPanel
- `projects/msp-tools/guru-rmm` (submodule) — advanced to commit 0253451

---

## Credentials & Secrets

None created or modified this session.

---

## Infrastructure & Servers

- **GuruRMM dashboard:** https://rmm.azcomputerguru.com
- **GuruRMM server:** 172.16.3.30:3001
- **Coord API:** http://172.16.3.30:8001/api/coord
- **Cascades tenant:** cascadestucson.com / admin@cascadesoftucson.onmicrosoft.com
- **Cascades DC:** CS-SERVER (cascades.local)

---

## Commands & Outputs

```bash
# SPEC-014 implementation commit
git commit -m "feat(spec-014): implement Windows event log viewer (Phase 1 PowerShell)"
# → [main 0253451]
git push origin main

# Submodule advance
git commit -m "chore(submodule): advance guru-rmm — SPEC-014 event log viewer implementation"

# Cascades billing — add_line_item (warranty, $0.00)
POST /api/v1/tickets/111233015/add_line_item
# → id: 42625866, product_id: 1049360, qty: 0.5, retail_cents: 0, user_id: 1750

# Invoice creation (top-level endpoint — sub-resource returned 404)
POST /api/v1/invoices { customer_id: 20149445, ticket_id: 111233015 }
# → invoice id: 1650452791, number: 67670, total: $0.00

# Ticket status → Invoiced
PUT /api/v1/tickets/111233015 { status: "Invoiced" }
# → status: "Invoiced"

# Bot alert
bash .claude/scripts/post-bot-alert.sh "[Syncro] Billed #32332 Cascades of Tucson - 0.5h warranty ..."
# → [OK] message_id=1509638644288196679
```

---

## Pending / Incomplete Tasks

- **SPEC-014 follow-up (Mike's):** Policy tab UI for event log watch rules on AgentDetail; push active watch rules to agent on initial WebSocket connect (agent currently only receives rules on write operations).
- **SPEC-012 implementation:** Sortable table headers, 4h estimate, no blockers — awaiting sprint assignment.
- **SPEC-013 (P3):** Deferred — revisit after file transfer (P2) ships.
- **Syncro sub-resource 404 pattern:** POST /tickets/{id}/comments and POST /tickets/{id}/invoice both return 404 for large-format ticket IDs. POST /invoices (top-level) works. Comments require GUI workaround for now. Needs investigation — may be a Syncro API version change or routing issue for the new ID format.
- **Cascades pending migration:** Ashley Jensen folder redirect verification, RECEPTIONIST-PC Q:/W: drives, NURSESTATION-PC HIPAA GPO, Nurses credential vault, Phase 3 domain joins, Entra Connect OU=Administrative expansion, M365 relicensing (31 seats Business Standard → Business Premium — time-sensitive).
- **BUG-001 (Windows thermal collection):** Blocked — LHM removed in v0.6.46. WMI ACPI (Approach 1) and NVAPI (Approach 2) not yet implemented.

---

## Reference Information

- **SPEC-014 spec:** `projects/msp-tools/guru-rmm/docs/specs/SPEC-014-windows-event-log-viewer.md`
- **SPEC-014 gururmm commit:** 0253451
- **Cascades ticket:** #32332 (internal: 111233015) — https://computerguru.syncromsp.com/tickets/111233015
- **Cascades invoice:** #67670 (id: 1650452791) — $0.00 warranty
- **Cascades warranty line item:** id 42625866 (product 1049360, qty 0.5, user_id 1750)
- **Cascades customer id:** 20149445 — prepay_hours: 24.25 (warranty does not deduct)
- **Bot alert message_id:** 1509638644288196679
- **AgentDetail.tsx Events tab:** Windows-only, `"events"` TabId, after Registry tab
- **event_log_watches table:** `server/migrations/047_event_log_watches.sql`
- **Dedup key format:** `evtlog:{agent_id}:{rule_id}:{event_id}`
- **EventLogViewer component:** `dashboard/src/components/EventLogViewer.tsx`
- **On-demand query endpoint:** `GET /api/agents/:id/eventlog`
- **Watch CRUD endpoints:** `GET/POST /api/event-log-watches`, `GET/PUT/DELETE /api/event-log-watches/:id`
