Birth Biologic: Merge + save Quality sync continuation state

This commit is contained in:
2026-06-30 15:28:44 -07:00
16 changed files with 812 additions and 6 deletions

View File

@@ -55,6 +55,10 @@ Create, update, close, comment on, and bill tickets in Syncro PSA.
**Always pass `"taxable": false` explicitly on labor line items.** Labor products are configured with `taxable: false` in Syncro, but `add_line_item` via API does not inherit the product's taxable setting — it posts the line item as `taxable: true` regardless. Always include `"taxable": false` in the payload to match the product's configured value.
**`product_category` is a CONTROLLED VOCABULARY — never invent one, never invoice a line blank.** Syncro categories are an admin-defined set; the CATEGORY column on every invoice line drives QuickBooks item mapping and revenue reporting. A blank category = an uncategorized line that breaks both. Two hard rules:
- **Never make up a category string.** Use ONLY a value that already exists in the tenant. To see the live set, enumerate distinct `product_category` across products (do NOT hardcode — it changes): `for p in $(seq 1 N); do curl -s "${BASE}/products?per_page=100&page=$p&api_key=${API_KEY}"; done | jq -r '.products[].product_category' | sort -u`. Known good values include `Labor`, `Software`, `Hardware`, `Product`, `Service`/`Services`, `Subscriptions`, `Contract`, `Discount`, `Exempt Labor`, `Prepay Hours`, `Other`, `Deposit`, `Default`. If none clearly fits, **ASK — do not guess**.
- **Never invoice a line whose product has `product_category: null`.** Before billing, `GET /products/<id>` and check `.product.product_category`. If it is `null`/blank, the product is mis-configured — STOP and flag it (a Windows/OS/software item → `Software`; a physical item → `Hardware`). Do not silently push the line out uncategorized. (Incident 2026-06-30, Cascades #32466/#32474 invoices 67887/67890: "Windows Pro Upgrade" product 23571919 has `product_category: null`, so every billed line showed a blank CATEGORY column. Winter corrected the records; the rule is here so the skill catches it next time.)
**`DELETE /schedules/{id}` destroys the recurring invoice template immediately — no confirmation, no undo.** Past generated invoices are unaffected but future billing stops. The schedule must be recreated manually with all line items if deleted accidentally. NEVER run destructive HTTP method probes against a live customer schedule — use ACG internal account (customer_id 15353550) for any testing. Incident: Russo Law Firm schedule 224454 deleted during API research 2026-05-26; recreated as 509659.
**Test articles — always prefix the subject/name with `[TEST]`.** Any ticket, estimate, appointment, or schedule created for testing or API research MUST have its subject or name prefixed with `[TEST]` (e.g. `[TEST] Schedule API research`, `[TEST] Estimate - hardware pricing`). This applies regardless of which customer account is used (including the ACG internal test account, customer_id 15353550). Test records must be instantly distinguishable from real customer work at a glance. If a test article was created without the prefix, PUT the subject to add it before continuing.
@@ -670,6 +674,8 @@ JSON
- `price_retail` — **must be set explicitly**; Syncro does NOT auto-populate from product rate via API
- `taxable` — **must be set explicitly**; always `false` for labor; `true` for taxable hardware
**Pre-flight every line item's category.** `GET /products/<id>` and confirm `.product.product_category` is non-null and is a real existing category (see the controlled-vocabulary rule above). A `null` category means the line will invoice with a blank CATEGORY column — STOP and flag the product instead of billing it. Never substitute an invented category.
**Do NOT remove line items after invoicing.** Leave them on the ticket.
**Labor product IDs** — always fetch `price_retail` live, never hardcode:

View File

@@ -41,6 +41,7 @@
- [INKY outbound breaks DMARC](reference_inky_outbound_breaks_dmarc.md) — Reverse-resolve DMARC rua failing IPs before blaming a sender: ipw-outbound.inkyphishfence.com / us.cloud-sec-av.com = INKY re-injection breaking DKIM+SPF. INKY is in-M365 (connectors+transport rules) per enrolled tenant, but hosting-level (IX/cPanel website) outbound also routes through it independent of M365 enrollment. Fix is INKY-side (outbound DKIM/SPF/ARC), not cPanel DNS.
- [Syncro prepay: full-GET only](feedback_syncro_prepay_full_get_only.md) — read prepay_hours ONLY from GET /customers/{id}; the customer search/list endpoint returns null/stale prepay. Never assert "no block" in a billing preview from search data.
- [Syncro priority/type format](feedback_syncro_priority_type_format.md) — every ticket create needs a number-prefixed priority ("2 Normal", not bare "Normal" which renders blank) AND a valid problem_type. Winter flagged #32193/#32194. Use the syncro skill's create flow.
- [Syncro line-item category](feedback_syncro_line_item_category.md) — product_category is a controlled vocab: never invent one, never invoice a line whose product has product_category:null (blank CATEGORY breaks QB mapping). Pre-flight GET /products/<id>. Winter fixed Cascades 67887/67890 "Windows Pro Upgrade".
- [RMM drive-map Explorer refresh](reference_rmm_drive_map_explorer_refresh.md) — drive mapped via RMM user_session works but the user's running Explorer won't show it until SHChangeNotify(DRIVEADD); also UNC \\ gets eaten in heredoc+jq, build it from [char]92.
- [Verify live before acting](feedback_verify_live_before_acting.md) — pull LIVE data (OMSA/iDRAC/live API) before acting on a hardware/infra flag; wiki/logs go stale. Cascades CS-SERVER "degraded RAID" was 9-day-stale (mirror self-recovered, SSDs bought needlessly). Windows can't see RAID member health.
- [Windows Pro upgrade billing](feedback_windows_pro_upgrade_billing.md) — ACG MAK key (vault infrastructure/windows-pro-mak, Mike's ACG key) for Home->Pro upgrades; RULE: invoice $99 PER machine activated, name the machine on the line item, bill after success. Each use consumes a MAK count.
@@ -153,6 +154,7 @@
- [Cascades](project_cascades.md) — Active state: Syncro ticket #110680053 + plan file (machine-specific path on Howard's box), admin accounts (sysadmin@=Howard, admin@=Mike — daily-driver, NOT break-glass), Phase-B caregiver CA pilot (SG-Caregivers-Pilot, group-scoped never tenant-wide), prepaid block ~37.5h (rate TBD), pilot cleanup checklist.
- [Cascades history](project_cascades_history.md) — fdeploy 502/ACL root cause (Flags=1211→187 fix), 2026-04-29 CA-rescoping decision (Howard pulled the brakes on tenant-wide), 2026-05-14 per-user-security-group decision rationale.
- [Cascades isolated-VLAN pattern](project_cascades_isolated_vlan_pattern.md) — pfSense: the GUEST VLAN (VLAN50/igc1.50) is the isolation template (4 any-proto quick rules: block 192.168.0.0/22 + 10.0.0.0/8 + 172.16.0.0/12, then pass any; public DNS via DHCP). VLAN20 is NOT isolated. Verify with `pfctl -sr`, not config.xml. Protocol MUST be Any (TCP-only leaks UDP). VOICE VLAN30 built to this 2026-06-17.
- [Cascades VLAN20 migration + routing](project_cascades_vlan20_migration_routing.md) — Staff machines/printers moving to VLAN20 (10.0.20.0/24). CS-SERVER couldn't reach VLAN20 printers because the LAN "allow LAN to any" rule policy-routes via WAN_Group → add a top LAN pass rule (src CS-SERVER 192.168.2.248, dst 10.0.20.0/24, gw=default) to bypass. pfSense SSH from VPN is blocked (do firewall in GUI). Printer client-map via GPO or SYSTEM `printui /ga` to dodge the 0x800702e4 PrintNightmare prompt; build UNC with [char]92.
- [Cascades KPI dashboard (parked)](project_cascades_kpi_dashboard.md) — Ashley Jensen wants one dashboard across their reporting SaaS (ALIS/QuickBooks/Bill.com/Relias/You've Got Leads/TELS/Focus HR/Helpany/POS). Power BI Gateway is the WRONG frame (on-prem only). Recommended Tier1→Tier2: scheduled exports → SharePoint → Power BI Pro, automate API-capable systems (Bill.com/QBO) via Power Automate later. Full notes: `clients/cascades-tucson/docs/proposals/kpi-dashboard.md`. Next: draft client one-pager.
- [Sync script bug — untracked files (RESOLVED)](project_sync_script_bug.md) — FIXED 2026-05-21: sync.sh now uses `git status --porcelain` for change detection (repo + vault).
- [MasterBooter Side Project](project_masterbooter.md) — Howard's Rust+Slint Windows deployment toolkit at C:\MasterBooter, separate from client work. Do not log to clients/.

View File

@@ -0,0 +1,15 @@
---
name: feedback_syncro_line_item_category
description: Syncro product_category is a controlled vocabulary — never invent one, never invoice a blank-category line
metadata:
type: feedback
---
When billing in Syncro, every invoice/ticket line item carries a CATEGORY (`product_category`) that is a **controlled, admin-defined vocabulary** and drives QuickBooks item mapping + revenue reporting. Two hard rules:
1. **Never make up a category.** Use only values that already exist in the tenant (e.g. `Labor`, `Software`, `Hardware`, `Product`, `Service`/`Services`, `Subscriptions`, `Contract`, `Discount`, `Exempt Labor`, `Prepay Hours`, `Other`, `Deposit`, `Default`). Enumerate the live set from `GET /products` rather than hardcoding. If none fits, ASK — do not guess.
2. **Never invoice a line whose product has `product_category: null`.** Pre-flight with `GET /products/<id>`; a blank category = an uncategorized line. Flag/fix the product (Windows/OS/software → `Software`, physical → `Hardware`) before billing.
**Why:** A blank or invented category silently breaks downstream accounting and makes humans clean up records. Incident 2026-06-30: Cascades "Windows Pro Upgrade" (product 23571919, `product_category: null`) invoiced on #32466/#32474 (invoices 67887/67890) with blank CATEGORY columns; Winter had to correct them. This keeps recurring as a skill-following gap.
**How to apply:** In the `/syncro` flow, before `add_line_item`/invoicing, GET the product and verify `product_category` is a real existing value. Rule lives in `.claude/commands/syncro.md` (taxable-rule block + add_line_item pre-flight). Related: [[feedback_syncro_billing]], [[feedback_syncro_preview_mandatory]], [[feedback_syncro_workflow]].

View File

@@ -0,0 +1,73 @@
---
name: project_cascades_vlan20_migration_routing
description: Cascades CSC ENT->VLAN20 migration — pfSense WAN_Group policy-route breaks LAN<->VLAN20; fix + printer-migration mechanics
metadata:
type: project
---
Cascades is migrating staff machines + printers off the flat old LAN (192.168.0.0/22,
"CSC ENT") onto the isolated **Staff VLAN 20 (10.0.20.0/24, gw 10.0.20.1)** ("CSCNET").
Printers are being re-IP'd to 10.0.20.x (static) and re-shared on the CS-SERVER print
server. Key operational facts learned 2026-06-30 (Howard, front-desk ET-5800 + Life
Enrichment Canon MF741CDW):
**pfSense gotcha (the big one):** CS-SERVER (on the old LAN) could not reach ANY VLAN 20
printer (.221/.220/.94/.78:9100) even though it pinged the VLAN20 gateway 10.0.20.1. Root
cause was NOT a block — the LAN "Default allow LAN to any" rule has **Gateway = WAN_Group**
(dual-WAN policy routing), so LAN->internal-VLAN traffic gets shoved out the WAN and dies.
**Fix = a pass rule at the TOP of the LAN interface** (Firewall/Rules/LAN), Source =
CS-SERVER 192.168.2.248, Dest = 10.0.20.0/24, protocol any, **Gateway = default** (do NOT
set WAN). This bypasses the policy route so internal traffic routes normally. Scoped to the
server's source IP => residents (own /28 VLANs) + guests (VLAN 50) can't match it (rule is
on the LAN interface, sourced from the server only). This also un-broke the already-migrated
Business Office/Life Enrichment/MC Reception shares. VLAN20->server (SMB) was already fine.
**pfSense SSH from the VPN is BLOCKED** (tcp/22 dropped; GUI 443 open). The `unifi-wifi`
skill's `pfsense-ssh.sh` therefore returns empty (it sends ssh stderr to /dev/null). Did the
rule via the GUI instead. To use the skill remotely later, add an OpenVPN-side allow for 22.
**Printer migration mechanics:**
- CS-SERVER side: repoint the share's port to TCP_10.0.20.<x>:9100 (Set-Printer -PortName),
drop the old 192.168.2.x port; keep the same ShareName so client mappings survive.
- Client side: mapping `\\CS-SERVER\<share>` as a standard domain user triggers a
PrintNightmare elevation prompt (HRESULT 0x800702e4) EVEN when the driver is already local
— see [[feedback_rmm_printer_elevation]] / errorlog. Promptless options: GPO printer
deployment (they already do this for caregivers — the scalable answer), or push as SYSTEM
via `rundll32 printui.dll,PrintUIEntry /ga /n"\\CS-SERVER\<share>"` (per-machine, appears
at the user's NEXT logon), or set Point-and-Print "Approved server = CS-SERVER" so user-
context maps are promptless+immediate.
- Old room-named shares (e.g. `1F-132-RecRoom-Canon`) were renamed on the server during
migration, leaving ORPHANED per-user client mappings; a spooler restart auto-drops them.
- Build UNC paths in RMM PowerShell with `[char]92`, not literal `\\` (jq/agent pipeline
mangles literal backslashes — [[feedback_windows_quote_stripping]]).
**Point-and-Print is the REAL promptless fix (proven 2026-06-30 on the LE machines).** The
0x800702e4 prompt AND the `/ga` per-machine path silently failing at logon (PrintService
event 513, error 0xBCB) are BOTH the same default `RestrictDriverInstallationToAdministrators`
(ON when unset) blocking the standard user from pulling the driver. We're domain admin, but
the *end user* isn't — so apply admin rights via the Point-and-Print policy:
`HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers` `RestrictDriverInstallationToAdministrators=0`
+ subkey `PointAndPrint` `Restricted=1,TrustedServers=1,ServerList=CS-SERVER,InForest=0,`
`NoWarningNoElevationOnInstall=1,UpdatePromptSettings=2` (scopes silent install to CS-SERVER
only). After that, `WScript.Network.AddWindowsPrinterConnection` in the user session is
promptless+immediate. **Correct durable fix = put this in a computer GPO fleet-wide** (the
caregiver machines already have it; that's why their printer GPO works), then deploy printers
via GPO. Existing GPOs: `CSC - Life Enrichment Printers`, `CSC - Printer Deployment`,
`CSC - Caregiver Workstation`, `CSC - Reception Workstation Policy` (the LE one likely still
pushes the OLD share name — repoint it to the new share).
**Driver/PDL trap — Canon MF741/743 = UFR II ONLY (not PCL).** The rebuilt `LifeEnrichment`
share was created with **Canon Generic Plus PCL6**; the MF741 can't parse PCL → spools OK,
nothing prints, panel shows **Error #822** (unsupported/corrupt data). Fix = use the **UFR II**
driver (`Canon Generic Plus UFR II V250`, INF cnlb0ma64.inf). CS-SERVER only had PCL6/PS3/XPS
staged; pulled UFR II from a client's DriverStore (`C:\Windows\System32\DriverStore\
FileRepository\cnlb0ma64.inf_amd64_*`) using the vaulted `cs-server` `sysadmin` domain-admin
cred. **Transfer direction matters:** CS-SERVER (192.168.2.x) CANNOT reach a client's C$
(client host-firewall scopes File/Print sharing to LocalSubnet, and CS-SERVER is off-subnet)
-> have the CLIENT push to `\\CS-SERVER\C$` instead (client->server SMB works). Then
`pnputil /add-driver <inf> /install`, `Add-PrinterDriver -Name "<exact INF model name>"`,
`Set-Printer -DriverName`. Get the exact driver name from the INF's quoted strings, not a guess.
When the server driver changes, refresh each client connection (Remove+AddWindowsPrinterConnection)
so it drops the stale cached PCL6 driver.
Related: wiki clients/cascades-tucson (network/VLANs), [[project-cascades-migration-plan]].

View File

@@ -0,0 +1,153 @@
## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-5070
- **Role:** admin
## Session Summary
Consolidated BirthBiologic's Quality content in SharePoint and reconciled it against the Datto
Workplace source of truth, then handled the migration-corruption fallout. The session began by
ensuring sysadmin@birthbiologic.com had access to the BirthBio SharePoint content sites: granted
owner+member on the canonical **Quality Systems Department** (QSD) site via its M365 group (he had
no access); confirmed existing owner access on Donor Services, Supply Management, and the old
Quality Department. The `[admin]` communication site and four empty `Quality Systems Department-*`
spoke sites have no M365 group, so they require a site-collection-admin grant via PnP (commands
DM'd to Mike; not runnable by the app — `Unsupported app only token` on SP REST).
Investigated why two Quality sites existed (`Quality Department`, created 2026-04-20, the original
migration landing site; `Quality Systems Department`, created 2026-06-02, canonical) and treated
Datto Workplace on ACG-DWP-X-BB as the authoritative source. Enumerated the Datto "Quality
Department" project (3,812 files / 28 GB). Reconciled QSD to Datto: removed 811 byte-identical
duplicate files (kept the Datto-aligned copy of each, verified by quickXorHash), removed 195
SP-only files older than the current week (kept 11 recent), and backfilled 31 files Datto had but
QSD lacked (30 via server-side copy from the old site, 1 — `2025.requested items for bone bank
audit.pdf` — uploaded from the Datto host via a pre-authenticated upload session). Final QSD ~3,766
files with 0 Datto files missing; the only remaining diff is 13 files under a renamed folder
(`Lab Log.2020 to Current` vs Datto's `2020 to 2024`).
Archived the duplicate site: copied the divergent `Processor Contact Information...Surgenex
8.21.24.xlsx` from the old site into QSD alongside its counterpart, ran a pre-delete safety delta
(0 unaccounted recent edits), then deleted the old `Quality Department` M365 group/site. The Tenant
Admin app 403s on group DELETE (has GroupMember write, not Group.ReadWrite.All); the **User Manager
app** performed the delete (HTTP 204). Group soft-deleted (restorable ~30 days; site recycle ~93 days).
Then handled migration corruption: the Surgenex xlsx Mike tried to open errored ("file format not
valid"). Diagnosed the byte-array->decimal-text corruption (header `80 75 3 4...` instead of
`PK\x03\x04`); the 64 KB QSD copy was corrupt, the 20 KB old-site copy clean and newer — deleted the
corrupt copy and renamed the clean one to canonical (after a WOPI lock cleared). A QSD-wide sweep
found **81 corrupt files** (60 docx/17 pdf/2 xlsx/2 doc), more than a parallel session's earlier 47
(my reconciliation propagated corrupt orphans via byte-preserving copy). Coordinated with the
parallel session (coord todo 28e3e7ab): appended reconciliation caveats + the live 81 count as
child todos, and graduated their recovery tool `bb-recover.py` from session scratchpad into the repo
at `clients/birth-biologic/scripts/`. Recovery itself remains deferred per Mike.
Finally, on a context switch, ran a read-only DC health verification on Peaceful Spirit's PST-SERVER
(192.168.0.2) via RMM: PST-SERVER healthy (all 5 FSMO, dcdiag clean, SYSVOL/NETLOGON shared, SYSVOL
Event 4602 from the 06-13 D4 restore), but PST-SERVER2 unreachable (ping fails, RPC 1722, ~16 days
stale) — Gate-4 DFSR work blocked until SERVER2 is back online.
## Key Decisions
- **Datto = source of truth** for the Quality reconciliation; the Datto-aligned path is the keeper
in any duplicate pair. Confirmed dupes removed only when a byte-identical Datto-aligned twin
remained in QSD (no unique data lost).
- **Did not bulk-delete by path.** Path comparison flagged 1,016 SP files "not in Datto," but 921
were the same files reorganized into different folders. Switched to hash-based dedup to avoid
destroying real data; surfaced this to Mike rather than executing the literal rule.
- **Backfill via server-side copy from the old site** (not Datto host upload) for 30 of 31 files —
cheaper and byte-identical; only the 1 file absent from the old site needed a host upload.
- **In-place reconstruct is the right recovery method** (the parallel session's bb-recover.py
PUTs recovered bytes to the same item id, preserving share links) — better than my re-copy
approach. Graduated their tool rather than writing a competing one.
- **Deleted the old site via the User Manager app** after the Tenant Admin app 403'd on group
delete — different app tier for the privilege.
- **Surgenex fix by swap, not reconstruct** — a clean newer copy existed on the old site, so
swapping it in was more reliable than reconstructing the corrupt bytes.
- **All destructive ops gated behind dry-runs + recycle bin** — every deletion recoverable ~93 days.
## Problems Encountered
- **Path-based "not in Datto" was misleading** — 921 of 1,016 were reorg copies; resolved by
switching to content-hash dedup.
- **Timestamp filter poisoned by migration copies** — 1,007 orphans carried today's date from my
earlier copy; used the old site's lastModified as the true-age signal instead.
- **Tenant Admin app cannot delete M365 groups** (403, GroupMember-only) and cannot manage SP site
state (`Unsupported app only token`) — used the User Manager app for group delete; SP site
lock/spoke-site grants pushed to Mike via PnP.
- **WOPI lock (HTTP 423)** blocked the Surgenex swap while the file was open in Excel; retried after
Mike closed it.
- **RMM stdout capped** the base64 of the 6.8 MB file (got 786 KB of 6.8 MB) — switched to a
pre-authenticated Graph upload session so the host PUT the bytes directly.
- **bash `${var/pat/repl}` mangled the upload URL** (& separators) — rewrote the PS script via
Python and dispatched with `jq --rawfile`.
- **Corruption scan kwarg bug** (`requests.get got multiple values for 'headers'`) — fixed the
retry wrapper to merge headers.
- **errorlog.md rebase conflict** on sync (concurrent top-prepends from another session) — resolved
keeping all entries.
- **bb-recover.py was stranded in another session's scratchpad** (session-local path) — graduated
to the repo so any machine can run it.
## Configuration Changes
- Created `clients/birth-biologic/docs/migration/2026-06-29-quality-dept-archival-plan.md` (then
marked COMPLETED).
- Created `clients/birth-biologic/scripts/bb-recover.py` (graduated recovery tool; pushed in 801ff788).
- Modified `errorlog.md` (friction: group-delete app gap; + conflict resolution).
- SharePoint (QSD, tenant 19a568e8-...): +sysadmin@ owner/member on QSD group; 811 dupes deleted;
195 stale SP-only deleted; 31 files backfilled; Surgenex corrupt copy deleted + clean copy renamed.
- Deleted old `Quality Department` M365 group/site (soft-deleted).
- Coord todos: appended children 5162f79f (caveats) + ac832238 (live 81 count) under 28e3e7ab.
## Credentials & Secrets
- No new credentials created or discovered. Used (read-only) from vault:
- `msp-tools/computerguru-tenant-admin.sops.yaml` field `credentials.client_secret` (app
709e6eed-0711-4875-9c44-2d3518c47063) — Graph app-only against BirthBio tenant.
- `msp-tools/computerguru-user-manager.sops.yaml` field `credentials.client_secret` (app
64fac46b-8b44-41ad-93ee-7da03927576c) — used for the M365 group delete (has Group.ReadWrite.All).
- bb-recover.py reads the Tenant Admin secret via env `BBSEC` (not embedded).
## Infrastructure & Servers
- **BirthBio M365 tenant:** 19a568e8-9e88-413b-9341-cbc224b39145
- **QSD site:** birthbiologic.sharepoint.com/sites/QualitySystemsDepartment (site id
...,3173c017-58bd-406a-8858-2c969667336f,...; single drive "Documents")
- **Old (deleted) group id:** f24b2e10-2d73-49d7-ab06-fe63065301d1 (QualityDepartment@), deletedDateTime 2026-06-29T22:23:15Z
- **Datto source host:** ACG-DWP-X-BB (172.16.3.45), RMM agent a4524e85-8a07-45d0-91b1-51ce7e2ca74a;
source tree `C:\Users\Public\Desktop\Datto Workplace Server Projects\Quality Department` (3,812 files)
- **Peaceful Spirit:** PST-SERVER 192.168.0.2 (all 5 FSMO, Server 2016, RMM 87293069-33b6-45e8-a68f-6811216cdb96);
PST-SERVER2 192.168.1.127/192.168.1.5 (RMM 5d2d7ba0-3903-4aa3-9e97-6ca4424ffe65) — UNREACHABLE.
## Commands & Outputs
- Graph app-only token: POST login.microsoftonline.com/{tenant}/oauth2/v2.0/token, scope
graph.microsoft.com/.default, grant client_credentials.
- Dedup keep-rule: same quickXorHash, keep copy whose path is in the Datto path set.
- Group delete: `DELETE /groups/{id}` — 403 via Tenant Admin app, **204 via User Manager app**.
- Pre-auth upload: `POST /drives/{id}/root:/{path}:/createUploadSession` -> host PUTs bytes to uploadUrl.
- DC verify (PST-SERVER): dcdiag Advertising/FSMOCheck/Services PASSED; `repadmin /replsummary`
PST-SERVER2 fails 5/5 error 1722; ping PST-SERVER2 = False.
## Pending / Incomplete Tasks
- **BirthBio QMS corruption recovery (DEFERRED, todo 28e3e7ab):** ~81 corrupt files in QSD. Run
`clients/birth-biologic/scripts/bb-recover.py birthbiologic.sharepoint.com:/sites/QualitySystemsDepartment`
(dry-run) then `--apply` (set BBSEC). Re-scan live; do NOT trust the saved 47-list. Then widen
the scan tenant-wide (Admin/Donor Services/Supply were in the same 6/26 corrupt batch).
- **Spoke + [admin] site access:** Mike to run the PnP grant (DM'd) for sysadmin@ on the 5 groupless sites.
- **Quality ticket write-up** (drafted in Mike's voice) NOT yet posted — needs ticket # (assumed
#32187), visibility (customer/internal), and billing decision.
- **Surgenex two-version note** moot now (corrupt copy removed; one clean file remains).
- **Peaceful Spirit:** PST-SERVER2 is dark — confirm whether it should be online; Gate-4 DFSR
(backlog drain, re-add folder/root targets) blocked until it is. If PST-SERVER was full-restored,
run a deeper post-restore DC pass (USN/invocationID, dcdiag /test:Replications, DNS SRV).
- **6 files** returned fetch-errors during the corruption sweep — re-check on recovery run.
## Reference Information
- Coord todos: 28e3e7ab-f77d-4d4f-b2e0-15f0254155ea (parent recovery), 5162f79f-de05-49d3-9f16-546f8d11c241
(caveats), ac832238-69f0-421f-9cc3-e9a7537d8ede (live 81 count).
- Pushed commit: 801ff788 (bb-recover.py graduation + errorlog).
- Recovery tool: clients/birth-biologic/scripts/bb-recover.py (BBSEC env = tenant-admin client_secret).
- Archival plan: clients/birth-biologic/docs/migration/2026-06-29-quality-dept-archival-plan.md
- Peaceful Spirit runbook: clients/peaceful-spirit/AD-DC2-REBUILD-RUNBOOK.md

View File

@@ -28,21 +28,21 @@
### `SG-Management-RW` → `\\CS-SERVER\Management`
**RW:** Meredith Kuhn, Ashley Jensen, Lauren Hasselman, Allison Reibschied, Megan Hiatt,
Crystal Rodriguez, Veronica Feller, Shelby Trozzi, Christina DuPras · ~~Tamra Matthews~~ *(leaving)*
Crystal Rodriguez, Veronica Feller, Shelby Trozzi, Christina DuPras · ~~Tamra Matthews~~ *(OFFBOARDED 2026-06-30)*
**RO (read-only):** Lois Lane, Christine Nyanzunda **[OPEN]**, Susan Hicks **[OPEN]**, John Trozzi, Lupe Sanchez **[OPEN]**
> No `SG-Management-RO` group exists — RO members need either a new RO group or a direct NTFS read ACL. **Decision needed.**
### `SG-Sales-RW` → `\\CS-SERVER\Sales` / `SalesDept`
**RW:** Meredith Kuhn, Ashley Jensen, Lauren Hasselman, Megan Hiatt, Crystal Rodriguez · ~~Tamra Matthews~~ *(leaving)*
**RW:** Meredith Kuhn, Ashley Jensen, Lauren Hasselman, Megan Hiatt, Crystal Rodriguez · ~~Tamra Matthews~~ *(OFFBOARDED 2026-06-30)*
**RO (`SG-Sales-RO`):** Shelby Trozzi
> **Two shares exist — `Sales` and `SalesDept`.** SalesDept holds the real history (20142026 reports, marketing). Confirm which the group maps to (or both), and what `Sales` is for.
### `SG-ALdocs-RW` → `\\CS-SERVER\ALdocs` *(share + group NOT created yet)*
**RW:** Lois Lane, Karen Rossini, Meredith Kuhn, Ashley Jensen, Megan Hiatt, Crystal Rodriguez · ~~Tamra Matthews~~ *(leaving)*
**RW:** Lois Lane, Karen Rossini, Meredith Kuhn, Ashley Jensen, Megan Hiatt, Crystal Rodriguez · ~~Tamra Matthews~~ *(OFFBOARDED 2026-06-30)*
> Must create the share + `SG-ALdocs-RW` group before assigning. Nurses (Lois/Karen) + Exec tier + Sales team.
### `SG-WebDocs-RW` → `\\CS-SERVER\WebDocs` *(share + group NOT created yet)*
**RW:** Megan Hiatt, Crystal Rodriguez, Meredith Kuhn, Ashley Jensen · ~~Tamra Matthews~~ *(leaving)*
**RW:** Megan Hiatt, Crystal Rodriguez, Meredith Kuhn, Ashley Jensen · ~~Tamra Matthews~~ *(OFFBOARDED 2026-06-30)*
> Must create the share + `SG-WebDocs-RW` group. Distinct from the retired DSM `web` station.
### `SG-Server-RW` → `\\CS-SERVER\Server`

View File

@@ -0,0 +1,43 @@
# Cascades — Printer / VLAN 20 Migration Map (GPO planning)
Living reference for the printer migration onto Staff VLAN 20 (10.0.20.0/24) and the
eventual **printer GPO** build. Update as machines/printers migrate. Started 2026-06-30 (Howard).
## How the GPO needs to be built (two layers)
1. **Point-and-Print policy (computer GPO, fleet-wide)** — REQUIRED prerequisite or any
GPO-pushed printer fails (PrintService event 513 / error 0xBCB) for standard users.
Set on `HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers`:
`RestrictDriverInstallationToAdministrators=0`; subkey `PointAndPrint`:
`Restricted=1, TrustedServers=1, ServerList=CS-SERVER, InForest=0,`
`NoWarningNoElevationOnInstall=1, UpdatePromptSettings=2` (scopes silent driver install
to CS-SERVER only). Caregiver machines already have this — that's why their printer GPO
works. Set manually 2026-06-30 on DESKTOP-ROK7VNM + DESKTOP-DLTAGOI; needs to be a GPO.
2. **Printer deployment** — GPP Printers / Deployed Printers mapping `\\CS-SERVER\<share>`
to the right users/OU/room. Existing GPO `CSC - Life Enrichment Printers` likely still
points at OLD share names — repoint. `CSC - Printer Deployment` is disabled/empty (do not use).
**Driver trap:** Canon MF741/743 are **UFR II only** — PCL6 produces Error #822 (spools, never
prints). Any GPO/share for those Canons MUST use `Canon Generic Plus UFR II V250` (INF cnlb0ma64.inf).
## Printer / machine map
| Printer (share / name) | Model | IP (VLAN20) | Driver | Machine | User(s) | Domain? | Status / GPO action |
|---|---|---|---|---|---|---|---|
| `\\CS-SERVER\FrontDesk` | Epson ET-5800 | 10.0.20.221 | EPSON ET-5800 Series | RECEPTIONIST-PC (frontdesk box, S/N MJ0KQHNP) | frontdesk | Domain (cascades.local) | DONE — share repointed, mapped, default. Add to GPO. |
| `\\CS-SERVER\LifeEnrichment` | Canon MF741CDW | 10.0.20.94 | Canon Generic Plus UFR II V250 | DESKTOP-DLTAGOI; DESKTOP-ROK7VNM | sharon.edwards; susan.hicks | Domain | DONE — UFR II driver fixed, mapped (not default). **Repoint `CSC - Life Enrichment Printers` GPO from old `1F-132-RecRoom-Canon` to `LifeEnrichment`.** |
| Dining Room Manager - Canon MF743CDW | Canon MF743CDW (MF741C/743C) | 10.0.20.228 | Canon Generic Plus UFR II V250 | DESKTOP-MD6UQI3 | dining manager (Alyssa) | **WORKGROUP — not domain-joined yet** | DONE as direct-IP (local) printer, default. **TODO: when DESKTOP-MD6UQI3 is domain-joined, add this printer to the GPO and map it to Alyssa's domain account.** |
| Chef Office - Brother MFC-9330CDW | Brother MFC-9330CDW | 10.0.20.236 | Brother MFC-9330CDW Printer | CHEF-PC | chef (all users) | **WORKGROUP — not domain-joined** | DONE as direct-IP (machine-wide / all users), default. **TODO: add to GPO + map to chef's domain account once CHEF-PC is domain-joined.** This is the Chef's printer in the Chef's office (distinct from the kitchen printer with the chefs). |
| Memory Care Front Desk - Epson ET-5800 (`\\CS-SERVER\MCReception`) | Epson ET-5800 | 10.0.20.78 | EPSON ET-5800 Series | MEMRECEPT-PC | memfrtdesk (+ other MemCare front-desk staff) | **WORKGROUP — not domain-joined** | Already shared on CS-SERVER as `MCReception`. Machine currently has the Epson via OLD vendor/WSD ports (`EP833571:ET-5800 SERIES` + WSD), NOT the static .78 — needs direct-IP to 10.0.20.78. **Mark for GPO: MemCare front-desk users (mostly the memfrtdesk machine). TODO: add to GPO + map to domain accounts once domain-joined.** |
| Memory Care MedTech - Brother MFC-L8900CDW (`\\CS-SERVER\MCMedTech`) | Brother MFC-L8900CDW | 10.0.20.74 | Brother MFC-L8900CDW series | RECEPTIONIST-PC (memcare box → **rename to MEMCARE-***); DESKTOP-LPOPV30 | memory care; karen rossini | **WORKGROUP** | DONE direct-IP machine-wide on both; old 192.168.2.53 + WSD connections removed; LPOPV30 default = new printer (was the old one); memcare box default unchanged (iR-ADV). MedTech room in Memory Care. **TODO: GPO + domain accounts once joined.** |
| `\\CS-SERVER\Kitchen` | Canon MF743CDW | 192.168.3.232 (pre-migration) | (verify) | (kitchen) | chefs | — | Kitchen printer (with the chefs). Not yet migrated to VLAN20 this round. |
## Machine rename TODO
- **RECEPTIONIST-PC** (the Memory Care box, "memory care" user, S/N MJ0KQH4R, agent 57f19e17) shares its hostname with the front-desk RECEPTIONIST-PC box — too hard to tell apart in the agent list. **Rename STAGED 2026-06-30 -> `MEMCARE-STATION`; applies on next reboot** (not forced; user was active). The OTHER RECEPTIONIST-PC (frontdesk user, S/N MJ0KQHNP) is the actual front desk.
## Notes
- Workgroup machines (DESKTOP-MD6UQI3, CHEF-PC) get **direct-IP local printers** for now
(no domain auth / no point-and-print needed). Once domain-joined, switch them to the
GPO-deployed `\\CS-SERVER\<share>` model and map to the domain account.
- Detailed how-to + pfSense routing fix: `.claude/memory/project_cascades_vlan20_migration_routing.md`
and session log `clients/cascades-tucson/session-logs/2026-06/2026-06-30-howard-vlan20-printer-migration.md`.

View File

@@ -0,0 +1,68 @@
# Offboarding Record — Tamra Matthews
**Date:** 2026-06-30 · **Performed by:** Howard Enos (ClaudeTools session) · **Authorized by:** Howard Enos
**Separation type:** Voluntary (left Cascades, June 2026) · **Role:** Move-In Coordinator (Marketing / Sales)
**Runbook:** `docs/security/termination-procedures.md`
## Identities handled
- **M365 (cloud-only):** `tamra.matthews@cascadestucson.com` — id `2d9cf0d1-2b0b-424e-9cd1-91eaac408837`
(`onPremisesSyncEnabled=null` — cloud-mastered object, NOT Entra-synced)
- **On-prem AD:** `Tamra.Matthews` (was `OU=Marketing,OU=Departments,DC=cascades,DC=local` — separate
object, NOT Entra-synced; renamed from Tamra.Johnson 2026-04-13)
- **ALIS:** Not handled this session — Move-In Coordinator role; confirm/disable ALIS staff profile if
she had clinical/ALIS access (open follow-up below).
## Actions completed (M365)
| # | Action | Result |
|---|---|---|
| 1 | Revoke active sign-in sessions | HTTP 200 |
| 2 | Block sign-in (`accountEnabled=false`) | confirmed false |
| 3 | Reset password (random, vaulted) | OK — no elevation needed (holds no admin role, **no PAA stranding**) |
| 4 | Convert mailbox → **SharedMailbox** | confirmed (`RecipientTypeDetails=SharedMailbox`) |
| 5 | Grant **FullAccess** to Crystal Rodriguez, Megan Hiatt, Meredith Kuhn, Ashley Jensen | all 4 confirmed FullAccess |
| 6 | Hide from GAL (`HiddenFromAddressListsEnabled=true`) | confirmed |
| 7 | Remove **O365 Business Standard** license (`f245ecc8…`) | confirmed 0 licenses — **frees 1 seat** |
| 8 | Remove from groups `Sales`, `All Cascades`, `SG-SSPR-Eligible` | HTTP 204 ×3 |
### AutoMapping — all 4 auto-attach (resolved)
- **All four delegates** (Crystal Rodriguez, Ashley Jensen, Megan Hiatt, Meredith Kuhn) have
**FullAccess with AutoMapping** — the shared mailbox auto-attaches in their Outlook and stays
until they remove it.
- **Gotcha encountered + fix:** the first pass added all 4 in a rapid loop with `AutoMapping:$true`;
only Crystal + Ashley persisted — Megan + Meredith rolled back to NONE (cmdlet echoed success).
Cause: rapid sequential AutoMapping writes contend on the *same* `msExchDelegateListLink` attribute
on Tamra's mailbox; the middle writes lost the race and a failed link-write aborts the whole
`Add-MailboxPermission` transaction. **Fix:** re-grant the missing users **one at a time, spaced
(~6-8s), with verify-and-retry** — `Remove-MailboxPermission` → poll until NONE →
`Add-MailboxPermission -AutoMapping $true` → confirm it persists. Both took on the first spaced
attempt. (The literal `msExchDelegateListLink` list is not exposed in the Exchange Operator REST
schema; persistence of an `AutoMapping:$true` grant is the success signal.)
## Actions completed (on-prem AD, CS-SERVER)
- `Set-ADAccountPassword -Reset` (random, vaulted)
- `Disable-ADAccount Tamra.Matthews` → Enabled=False
- Group memberships: already 0 (no explicit groups) — nothing to strip
- `Move-ADObject``CN=Tamra Matthews,OU=Excluded-From-Sync,DC=cascades,DC=local`
## Retention / compliance
- **No Litigation Hold applied.** Decision (Howard, 2026-06-30): although Move-In Coordinator is a
resident-intake / PHI-adjacent role, Howard authorized the same posture as the Alma Montt
offboarding — **shared-mailbox conversion + zero-deletion** (no mailbox deleted), license removed to
free the seat. Mailbox is preserved under default MRM retention; revisit if her PHI-access
determination or a legal hold changes. Litigation Hold remains available later (tenant has Business
Premium / Exchange Plan 2) if the determination changes.
- Passwords stored for emergency recovery/audit only: vault `clients/cascades-tucson/tamra-matthews`
(`m365_password`, `ad_password`). **Do NOT re-enable without authorization.**
## Open follow-ups
- [ ] **ALIS staff profile** — confirm whether Tamra had ALIS access; if so, disable the staff record
(audit record stays). Her M365 SSO tie is already severed by the sign-in block.
- [x] ~~AutoMapping for Megan Hiatt + Meredith Kuhn~~**RESOLVED 2026-06-30**: re-granted one at a
time (spaced) so AutoMapping persisted. All 4 now auto-attach.
- [ ] **Reconcile** Tamra out of forward-looking plans/rosters. Share roster
`docs/migration/share-group-roster-proposed-2026-06-25.md` already shows her struck-through
*(leaving)*; `docs/servers/active-directory.md` OU=Marketing row updated to disabled +
Excluded-From-Sync. April/May questionnaires, CSVs, and the staff roster
`reports/cascades-staff-2026-04-22.csv` left as historical record.
- [ ] **Note (separate):** Megan Hiatt's M365 account carries a `CREDENTIAL_STUFFING_ACTIVE` flag in
the April tenant inventory — unrelated to this offboarding, but worth a breach check.

View File

@@ -75,7 +75,7 @@
|---------------|------|----------|-------|
| Megan.Hiatt | Megan Hiatt | Sales Director | M365: Sales@ |
| Crystal.Rodriguez | Crystal Rodriguez | Sales Associate | PC: CRYSTAL-PC. M365: Sales@ |
| Tamra.Matthews | Tamra Matthews | Move-In Coordinator | Renamed from Tamra.Johnson (2026-04-13) |
| ~~Tamra.Matthews~~ | ~~Tamra Matthews~~ | ~~Move-In Coordinator~~ | **OFFBOARDED 2026-06-30** — disabled, moved to `OU=Excluded-From-Sync`. See `docs/security/offboarding-2026-06-30-tamra-matthews.md` |
**OU=Resident Services**
| SamAccountName | Name | Position | Notes |

View File

@@ -0,0 +1,53 @@
# Breach Re-Check — megan.hiatt@cascadestucson.com
**Date:** 2026-06-30 · **Performed by:** Howard Enos (ClaudeTools session)
**Tenant:** Cascades of Tucson (cascadestucson.com, `207fa277-e9d8-4eb7-ada1-1064d2221498`)
**Object ID:** `ab306d53-6d6c-4f8f-a982-f4f571722178`
**Why:** Megan's account carried a `CREDENTIAL_STUFFING_ACTIVE` marker in the April tenant
inventory. This re-check verifies whether the April remediation held and whether the campaign is
still active. Read-only; no actions taken.
## Verdict — CLEAN. April remediation held; attack no longer active.
The April credential-stuffing campaign (119 malicious sign-in attempts over 30 days from 7
EU/UK IPs, all blocked at error 50053) has **ceased**, and the hardening applied in April is
**still in place**. No compromise indicators.
## April remediation — current status (did it hold?)
| April control | Current state (2026-06-30) | Held? |
|---|---|---|
| Disable SMTP AUTH on Megan's mailbox | `SmtpClientAuthenticationDisabled=true` | **Yes** |
| Disable IMAP | `ImapEnabled=false` | **Yes** |
| Disable POP | `PopEnabled=false` | **Yes** |
| Rotate password | Last change `2026-05-28` (post-April; rotated) | **Yes** |
| MFA = Authenticator (not SMS) | Methods: password + microsoftAuthenticator only; **no SMS**, no new method | **Yes** |
| Tenant anti-spam / anti-phish hardening (SPF hard-fail, mailbox-intelligence quarantine, first-contact tips) | Applied tenant-wide in April (Default policies) | (tenant-level, unchanged) |
EWS / ActiveSync / OWA / MAPI remain enabled — same as April; those are modern-auth capable, not
basic-auth bypass paths.
## Live breach check (10-point) — all clean
| # | Check | Result |
|---|---|---|
| Sign-ins (30d) | **0 interactive, 0 non-US** (was 119 malicious + 16 US-success in April) | No active attack; no foreign success ever |
| Account | `accountEnabled=true`, cloud-only | normal |
| Auth methods | password + Microsoft Authenticator (2) | no new/weak method |
| Inbox rules | 1 visible + 4 hidden — all benign (Junk default, 2 OOF system, user "Cascade of Tucson" move rule) | no forward/redirect/delete |
| Mailbox permissions | 0 non-SELF | no delegates |
| SendAs | 0 non-SELF | none |
| Forwarding | `ForwardingAddress=null`, `ForwardingSmtpAddress=null` | not forwarding |
| OAuth grants | 5 — Outlook Mobile ×2, third-party OIDC SSO, Contacts.Read (April set) **+ ALIS SSO SP `e1cae4ad…` User.Read** (expected, June 3 ALIS rollout) | benign |
| Directory audits (30d) | 0 | no admin tampering |
| Risk detections | 0 (risky-user read still `Forbidden` — known `IdentityRiskyUser.Read.All` consent gap, not a finding) | — |
## Notes
- **`CREDENTIAL_STUFFING_ACTIVE` is a stale April marker**, not a live signal — the campaign is no
longer hitting (0 attempts in 30d). It reflects April state captured in the tenant inventory.
- The only April recommendation not confirmed implemented is **C1 — Conditional Access US-only
geo-block** for office users. It is now **optional/low-urgency**: every stuffing attempt was
already blocked by MFA + MS IP-reputation, and the campaign has stopped. Worth scheduling as
baseline hardening but not an active risk.
- Raw artifacts: `/tmp/remediation-tool/207fa277-…/user-breach/megan_hiatt_cascadestucson_com/`.

View File

@@ -0,0 +1,135 @@
## User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech
## Session Summary
Offboarded **Tamra Matthews** (Move-In Coordinator, Marketing/Sales) from Cascades of Tucson
after she left the facility in June 2026. Followed the canonical termination runbook
(`docs/security/termination-procedures.md`) and the Alma Montt offboarding (2026-06-25) as the
proven pattern. Work spanned the M365 cloud tenant (via the `remediation-tool` skill) and the
on-prem AD domain controller CS-SERVER (via the `rmm` skill); both passwords were vaulted.
Established live pre-state first: Tamra's M365 object (`tamra.matthews@cascadestucson.com`,
id `2d9cf0d1-2b0b-424e-9cd1-91eaac408837`) is **cloud-only** (`onPremisesSyncEnabled=null`) with a
single O365 Business Standard license and membership in `Sales`, `All Cascades`, and
`SG-SSPR-Eligible`. The on-prem `Tamra.Matthews` is a **separate, non-synced** object in
`OU=Marketing,OU=Departments` with no explicit group memberships. All four mailbox-delegate
recipients (Crystal Rodriguez, Megan Hiatt, Meredith Kuhn, Ashley Jensen) resolved as
UserMailboxes; all three target groups were static (not dynamic), so removable.
Before mutating anything, surfaced one genuine compliance fork to Howard: unlike Alma (no PHI),
Move-In Coordinator is a resident-intake / PHI-adjacent role, so the runbook's 7-yr Litigation
Hold could apply. Howard authorized the **Alma-style posture** — shared-mailbox + zero-deletion,
free the seat, **no litigation hold** (revisit if the PHI determination changes; hold remains
available since the tenant has Business Premium/Exchange Plan 2).
Executed the M365 sequence (revoke sessions, block sign-in, vaulted password reset, convert to
SharedMailbox, grant the 4 delegates FullAccess+AutoMapping, hide from GAL, remove license, strip
3 groups), then the on-prem AD lockdown (reset password, Disable-ADAccount, move to
`OU=Excluded-From-Sync`). Verified every step against live state. Vaulted both passwords, wrote the
offboarding record, reconciled the AD current-state doc + share roster, and synced. Per Howard's
direction, AutoMapping was then fixed for all 4 delegates and a `/save` run before proceeding to a
breach check on Megan Hiatt.
## Key Decisions
- **No litigation hold despite PHI-adjacent role** — Howard explicitly authorized the same
preserve-but-no-hold posture as Alma. Documented the deviation in the offboarding record so the
PHI-role/no-hold decision is auditable.
- **Treated the M365 object as cloud-only and the AD object as separate** — `onPremisesSyncEnabled=null`
confirmed the M365 account is cloud-mastered; the domain lockdown was a distinct operation on
CS-SERVER (same split as Alma).
- **Removed license only AFTER shared-mailbox conversion** — per runbook, so the mailbox is never a
licensed-then-unlicensed user mailbox at risk of default-retention deletion.
- **Fixed AutoMapping by spaced, one-at-a-time re-grants** rather than leaving Megan/Meredith on
manual-add — Howard wanted the box to auto-attach and stay.
## Problems Encountered
- **AutoMapping silently rolled back for 2 of 4 delegates.** The first pass added all 4 in a rapid
loop with `AutoMapping:$true`; the cmdlet echoed `[FullAccess]` for all 4, but a readback showed
only Crystal + Ashley persisted — Megan + Meredith were NONE. Cause: rapid sequential AutoMapping
writes contend on the same `msExchDelegateListLink` multivalued attribute on Tamra's mailbox; a
failed link-write aborts the whole `Add-MailboxPermission` transaction, so the middle writes lost
the race and rolled back entirely. **Resolved** by re-granting one user at a time, spaced ~6-8s,
with `Remove-MailboxPermission` → poll-until-NONE → `Add-MailboxPermission -AutoMapping $true`
verify-persists. Both took on the first spaced attempt. Logged to `errorlog.md` (--friction).
- **Graph read-replica lag** — immediately after `PATCH accountEnabled=false` (HTTP 204), a verify
read still showed `accountEnabled=true`; a re-read seconds later showed false. Not a failure, just
eventual consistency. Same lag bit the Meredith remove (still showed FullAccess right after the
remove), handled with a poll-until-NONE loop.
- **msExchDelegateListLink not readable via REST** — the Exchange Operator REST `Get-Mailbox` schema
doesn't expose the attribute, so AutoMapping can't be directly read; persistence of an
`AutoMapping:$true` grant (no rollback) is the success signal used instead.
## Configuration Changes
- **Created:** `clients/cascades-tucson/docs/security/offboarding-2026-06-30-tamra-matthews.md`
(offboarding record).
- **Created:** `clients/cascades-tucson/session-logs/2026-06/2026-06-30-howard-tamra-matthews-offboarding.md`
(this log).
- **Created (vault):** `clients/cascades-tucson/tamra-matthews.sops.yaml` (encrypted; m365_password +
ad_password + metadata). Committed + pushed to the vault repo.
- **Edited:** `clients/cascades-tucson/docs/servers/active-directory.md` — OU=Marketing Tamra.Matthews
row struck + annotated OFFBOARDED/disabled/Excluded-From-Sync.
- **Edited:** `clients/cascades-tucson/docs/migration/share-group-roster-proposed-2026-06-25.md`
Tamra `(leaving)``(OFFBOARDED 2026-06-30)` (4 lines).
- **Appended:** `errorlog.md` — EXO AutoMapping rollback friction entry.
## Credentials & Secrets
- **Tamra Matthews offboarding passwords** (emergency/audit only; **DO NOT re-enable without
authorization**): vault `clients/cascades-tucson/tamra-matthews` — fields `m365_password`,
`ad_password`. Generated random, complexity-guaranteed; not pasted into chat/tickets. Scratchpad
plaintext was scrubbed after vaulting.
## Infrastructure & Servers
- **M365 tenant:** cascadestucson.com — Tenant ID `207fa277-e9d8-4eb7-ada1-1064d2221498`
- **Tamra M365 object id:** `2d9cf0d1-2b0b-424e-9cd1-91eaac408837` (cloud-only)
- **Removed license SKU:** `f245ecc8-75af-4f8e-b61f-27d8114de5f3` (O365 Business Standard) — seat freed
- **Delegate mailbox GUIDs:** megan.hiatt `448588b1-cb16-41e4-95b3-a496ebf0c7e8`; meredith.kuhn
`02ebe159-818e-4814-a72a-3620dea944a0`
- **Groups removed from:** `Sales` `757e7628-ff38-477c-a5a1-71b27e94d365` (Unified),
`All Cascades` `0344d8ae-bf3c-4b2f-b30b-c9fa93a20aa2` (Unified),
`SG-SSPR-Eligible` `d6044864-a0ef-4c30-ba37-cdba7074437e` (security)
- **On-prem DC:** CS-SERVER (192.168.2.254), `cascades.local`; GuruRMM agent id (live this session)
`c39f1de7-d5b6-45ae-b132-e06977ab1713`. Tamra final DN
`CN=Tamra Matthews,OU=Excluded-From-Sync,DC=cascades,DC=local`, Enabled=False.
- **Remediation apps used:** Security Investigator (`bfbc12a4`, Graph+EXO read), Exchange Operator
(`b43e7342`, EXO write), User Manager (`64fac46b`, Graph user/group/license write), Tenant Admin
(`709e6eed`, password reset — no PAA elevation needed for a non-admin target).
## Commands & Outputs
- Block sign-in: `PATCH /users/{id}` `{"accountEnabled":false}` → HTTP 204 (verify lagged true→false).
- Revoke sessions: `POST /users/{id}/revokeSignInSessions` → HTTP 200.
- Password reset: `reset-password.sh … tamra.matthews …``[OK] no elevation needed` (non-admin).
- Convert shared: EXO `Set-Mailbox -Type Shared``RecipientTypeDetails=SharedMailbox`.
- Grants (final): all 4 `Get-MailboxPermission -User <u>``["FullAccess"]`.
- License remove: `POST /users/{id}/assignLicense` removeLicenses=[f245ecc8…] → 0 licenses.
- Group removes: `DELETE /groups/{gid}/members/{id}/$ref` → 204 ×3.
- AD lockdown (RMM cmd `a788a445…`, exit 0): `Set-ADAccountPassword -Reset` + `Disable-ADAccount` +
`Move-ADObject``Enabled=False`, DN in `OU=Excluded-From-Sync`, Groups=0.
- AutoMapping fix: per-user `Remove-MailboxPermission` → poll NONE → `Add-MailboxPermission -AutoMapping $true`
→ persists (Megan + Meredith, first spaced attempt).
## Pending / Incomplete Tasks
- **ALIS** — Howard handling; unsure Tamra used ALIS. If she had a staff profile, disable it
(M365 SSO tie already severed by the sign-in block).
- **Breach check on Megan Hiatt** — NEXT step this session. Megan's account carried a
`CREDENTIAL_STUFFING_ACTIVE` marker in the April tenant inventory; Howard recalls an April
remediation that should have stopped it — verify it held (`/remediation-tool check megan.hiatt@…`).
- **Litigation hold** — not applied (Howard authorized); revisit only if Tamra's PHI-access
determination or a legal hold changes.
## Reference Information
- Offboarding record: `clients/cascades-tucson/docs/security/offboarding-2026-06-30-tamra-matthews.md`
- Termination runbook: `docs/security/termination-procedures.md`
- Prior offboarding (pattern): `docs/security/offboarding-2026-06-25-alma-montt.md`
- Vault: `clients/cascades-tucson/tamra-matthews`
- Cascades wiki: `wiki/clients/cascades-tucson.md`

View File

@@ -0,0 +1,116 @@
## User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech
## Session Summary
Migrated the front-desk Epson ET-5800 and the Life Enrichment Canon MF741CDW onto the new Staff VLAN 20 (10.0.20.0/24 / "CSCNET"), as part of the ongoing move of staff machines + printers off the flat old LAN ("CSC ENT", 192.168.0.0/22). All work driven via GuruRMM (CS-SERVER + the client agents) plus one pfSense GUI change.
First, the front-desk ET-5800: confirmed RECEPTIONIST-PC is two physical boxes (frontdesk = serial MJ0KQHNP at 10.0.20.102; the other is Memory Care reception). Repointed the CS-SERVER `FrontDesk` share from `TCP_192.168.2.147` to `TCP_10.0.20.221` (printer set to static .221 by Howard), removed the old port, removed the stale local `ET-5800 Series(Network)` printer from the frontdesk box, mapped `\\CS-SERVER\FrontDesk` and set it default. Test page printed.
Hit a hard blocker: CS-SERVER could not reach ANY VLAN 20 printer (.221/.220/.94/.78:9100) even though it pinged the VLAN20 gateway 10.0.20.1. Root cause was not a firewall block — the LAN "Default allow LAN to any" rule has Gateway = WAN_Group (dual-WAN policy routing), shoving LAN->internal-VLAN traffic out the WAN. Fixed with one pass rule at the top of the pfSense LAN interface (source CS-SERVER 192.168.2.248, dest 10.0.20.0/24, gateway = default). This also un-broke the already-migrated Business Office/Life Enrichment/MC Reception shares. (pfSense SSH from the VPN is blocked, so the unifi-wifi pfsense-ssh.sh skill returned empty; did the rule in the GUI.)
Then Life Enrichment (room 132, users sharon.edwards @ DESKTOP-DLTAGOI and susan.hicks @ DESKTOP-ROK7VNM). The old per-user mapping `\\CS-SERVER\1F-132-RecRoom-Canon` was orphaned (share renamed to `LifeEnrichment` on the server). Added `\\CS-SERVER\LifeEnrichment` for both users (defaults left as `Copy Room` per Howard). Mapping initially failed with the PrintNightmare elevation prompt (0x800702e4) and the per-machine `/ga` path silently failed at logon (PrintService event 513 / error 0xBCB) — both are the default `RestrictDriverInstallationToAdministrators` blocking the standard user from pulling the driver. Resolved by setting the Point-and-Print policy (scoped to CS-SERVER) on both machines, after which the user-session add was promptless+immediate.
Final blocker: jobs spooled but nothing printed; the Canon panel showed Error #822. Diagnosis: the rebuilt `LifeEnrichment` share used the wrong PDL — `Canon Generic Plus PCL6` — but the MF741 is UFR II only (can't parse PCL). CS-SERVER only had PCL6/PS3/XPS staged. Pulled the `Canon Generic Plus UFR II V250` driver from a client's DriverStore to CS-SERVER (using the vaulted sysadmin domain-admin cred), installed it, and switched the share to it. Test prints from the server, Susan's machine, and Sharon's machine all printed (Howard confirmed at the printer). Both LE machines verified on the UFR II driver, defaults unchanged.
## Key Decisions
- **pfSense fix = policy-route bypass, not an allow rule.** The traffic was never blocked; it was misrouted out the WAN by the LAN catch-all's WAN_Group gateway. Added a top LAN pass rule with gateway=default scoped to CS-SERVER's source IP, so residents (per-unit /28 VLANs) and guests (VLAN 50) can't match it (different ingress interface + different source). Did it in the GUI because pfSense SSH/22 is blocked from the OpenVPN subnet.
- **Point-and-Print policy is the correct "apply our admin rights" fix**, not making end users local admins and not a security bypass. Scoped silent driver install to CS-SERVER only. This is the prerequisite a printer-deployment GPO needs; without it a GPO-pushed printer fails the same 0xBCB way. The caregiver machines already have this (why their printer GPO works).
- **Server-share model kept** (Howard's preference) rather than falling back to direct-IP printers, even when the UFR II driver wasn't on the server — pulled the driver across with the domain-admin cred instead.
- **Driver transfer direction = client -> server.** CS-SERVER (192.168.2.x) cannot reach a client's C$ (client host-firewall scopes File/Print sharing to LocalSubnet; CS-SERVER is off-subnet). The client pushes to `\\CS-SERVER\C$` instead.
- **Defaults left untouched** on the LE users per explicit instruction (they keep `Copy Room`).
## Problems Encountered
- **CS-SERVER could not reach VLAN 20 printers** -> LAN "allow LAN to any" rule policy-routed via WAN_Group. Fixed with top LAN pass rule, gateway=default, src CS-SERVER, dst 10.0.20.0/24.
- **pfSense SSH from VPN timed out** (tcp/22 dropped, GUI 443 open) -> the unifi-wifi pfsense-ssh.sh skill silently returned empty (sends ssh stderr to /dev/null). Worked around via the pfSense GUI.
- **Printer mapping elevation prompt (0x800702e4)** for standard users, and per-machine `/ga` silently failing at logon (event 513 / 0xBCB) -> set Point-and-Print policy (RestrictDriverInstallationToAdministrators=0 + trusted CS-SERVER) -> promptless.
- **LE Canon: spooled but nothing printed, Error #822** -> wrong driver (PCL6) on an UFR-II-only device -> installed Canon Generic Plus UFR II V250 on CS-SERVER and switched the share.
- **Stale cached driver on clients** after the server driver swap -> refreshed each client connection (Remove + AddWindowsPrinterConnection) so it dropped PCL6 and pulled UFR II.
- **Backslash mangling** in RMM PowerShell payloads (literal `\\` -> single `\` through jq/agent) -> build UNC with `[char]92`. Logged to errorlog as friction.
## Configuration Changes
- pfSense (Cascades, 192.168.0.1): added LAN firewall rule (top) — Pass, IPv4, proto Any, Source 192.168.2.248, Dest 10.0.20.0/24, Gateway = default. Description "CS-SERVER to VLAN20 (full server access, bypass WAN policy-route)".
- CS-SERVER `FrontDesk` share: port `TCP_192.168.2.147` -> `TCP_10.0.20.221`; old port removed; ShareName `FrontDesk` unchanged.
- CS-SERVER `Life Enrichment - Canon MF741CDW` share (`LifeEnrichment`): driver `Canon Generic Plus PCL6` -> `Canon Generic Plus UFR II V250` (port TCP_10.0.20.94 unchanged). Installed UFR II driver package (now `oem15.inf`); staging files left at `C:\Temp\ufr2drv` on CS-SERVER (can be deleted).
- DESKTOP-ROK7VNM (susan.hicks) + DESKTOP-DLTAGOI (sharon.edwards): set Point-and-Print policy (HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers): `RestrictDriverInstallationToAdministrators=0`; subkey `PointAndPrint`: `Restricted=1, TrustedServers=1, ServerList=CS-SERVER, InForest=0, NoWarningNoElevationOnInstall=1, UpdatePromptSettings=2`. Added `\\CS-SERVER\LifeEnrichment` (UFR II), removed orphaned `1F-132-RecRoom-Canon`. Defaults unchanged.
- RECEPTIONIST-PC (frontdesk box, MJ0KQHNP): removed local `ET-5800 Series(Network)`; mapped `\\CS-SERVER\FrontDesk`, set default.
- Memory: updated `.claude/memory/project_cascades_vlan20_migration_routing.md` (+ MEMORY.md index line). Errorlog: 2 friction entries (UNC backslash, rmm printer elevation).
## Credentials & Secrets
- Used the vaulted CS-SERVER admin: `clients/cascades-tucson/cs-server.sops.yaml` (username `sysadmin`, domain admin on cascades.local) — for the cross-machine driver pull. No new credentials created or discovered. Not exposed here (already vaulted).
## Infrastructure & Servers
- CS-SERVER 192.168.2.254 / 192.168.2.248 (DC, DNS, File + Print server), GuruRMM agent `c39f1de7-d5b6-45ae-b132-e06977ab1713`. Routes to VLAN20 from .248.
- pfSense 192.168.0.1 (Plus 25.07). LAN = 192.168.0.0/22. Dual-WAN (WAN_Group). VLAN 20 (Staff/Internal) 10.0.20.0/24 gw 10.0.20.1. SSH/22 blocked from OpenVPN subnet (GUI 443 open).
- Printers (VLAN 20, static): Front Desk Epson ET-5800 = 10.0.20.221 (RAW 9100); Life Enrichment Canon MF741CDW = 10.0.20.94 (RAW 9100, UFR II); Business Office Brother = 10.0.20.220 (was powered off this session); MC Reception Epson ET-5800 = 10.0.20.78; Life Enrichment also reachable .94.
- Agents: DESKTOP-ROK7VNM (susan.hicks) `4832ff97-196d-4de5-a3b9-dadf9adb2c7a`; DESKTOP-DLTAGOI (sharon.edwards) `4bdf92f6-7a84-4b71-81df-75d59febf39d`; RECEPTIONIST-PC frontdesk box `2e8d8b73-82f6-4151-a3ce-879c55de4b82`; MemCare reception box `57f19e17-8792-46cc-b9fd-f1909836cd17`.
## GPO Reference (for the printer-GPO work Howard flagged)
Existing GPOs on cascades.local (from CS-SERVER `Get-GPO -All`):
`CSC - Always Wait For Network`, `CSC - Caregiver Device Lockdown`, `CSC - Caregiver Workstation`, `CSC - Drive Mappings`, `CSC - Folder Redirection`, `CSC - Folder Redirection (LE)`, **`CSC - Life Enrichment Printers`**, **`CSC - Printer Deployment`**, `CSC - Reception Workstation Policy`, `CSC - Security Baseline`, `CSC - Windows Update`, `Power Options`, + the two Defaults.
For the printer GPO to work promptlessly, it needs TWO layers:
1. **Point-and-Print policy (computer GPO, fleet-wide)** = the exact registry values set manually this session (see Configuration Changes). Caregiver machines already have it; staff/LE machines did not. Put this in a computer GPO so every staff machine can silently install CS-SERVER printers.
2. **Printer deployment** = GPP Printers item / Deployed Printers mapping `\\CS-SERVER\<share>` to the right users/OU/room. `CSC - Life Enrichment Printers` likely still references the OLD share name (`1F-132-RecRoom-Canon`) — repoint it to `\\CS-SERVER\LifeEnrichment`. NOTE: `CSC - Printer Deployment` is the known disabled/empty/reference-only one.
Per-user/room printer map confirmed this session (extend as machines migrate):
- Front desk (RECEPTIONIST-PC frontdesk box) -> `\\CS-SERVER\FrontDesk` (Epson ET-5800, default).
- Life Enrichment room 132 -> `\\CS-SERVER\LifeEnrichment` (Canon MF741CDW, **UFR II driver**) for sharon.edwards + susan.hicks (NOT default; their default stays Copy Room).
CRITICAL driver note for any GPO deploying the LE Canon: it MUST use **Canon Generic Plus UFR II V250** (INF cnlb0ma64.inf), NOT PCL6 — PCL6 produces Error #822 (nothing prints).
## Commands & Outputs
- pfSense verify (RMM, CS-SERVER): `10.0.20.221:9100=True`, `.94=True`, `.78=True`, `.220=False` (powered off). VLAN20->CS-SERVER 445 = True.
- Error #822 confirmation: raw PCL to 10.0.20.94:9100 -> Error 822 (printer can't parse PCL). `Get-PrinterDriver` on CS-SERVER had only PCL6/PS3/XPS Canon; client had `Canon Generic Plus UFR II V250` (InfPath cnlb0ma64.inf).
- Driver install: client pushed package to `\\CS-SERVER\C$\Temp\ufr2drv` (159 files); `pnputil /add-driver cnlb0ma64.inf /install` -> oem15.inf; `Add-PrinterDriver -Name "Canon Generic Plus UFR II V250"`; `Set-Printer -DriverName`.
- PrintService event 513 / error 0xBCB on DESKTOP-ROK7VNM = per-machine `/ga` failing to install driver for the standard user (pre Point-and-Print fix).
## Pending / Incomplete Tasks
- **Printer GPO (Howard's next focus):** put the Point-and-Print policy into a computer GPO fleet-wide; repoint `CSC - Life Enrichment Printers` to `\\CS-SERVER\LifeEnrichment` (UFR II); build out per-room printer-deployment items as the who-needs-what map firms up.
- **Business Office printer (10.0.20.220)** was powered off / unreachable this session — verify it when onsite.
- **Cascades printer skill (Howard's idea):** package this migration's how-to (VLAN routing/pfSense bypass, server-share repoint, Point-and-Print policy, UFR II driver, [char]92 UNC) into a reusable skill so future printer adds are one call.
- Optional cleanup: delete `C:\Temp\ufr2drv` on CS-SERVER (driver staging files).
- pfSense SSH-from-VPN still blocked (separate; only needed to use the pfsense-ssh.sh skill remotely).
## Reference Information
- GuruRMM API: http://172.16.3.30:3001 (vault infrastructure/gururmm-server.sops.yaml).
- Memory: `.claude/memory/project_cascades_vlan20_migration_routing.md`.
- UFR II driver: `Canon Generic Plus UFR II V250`, INF `cnlb0ma64.inf`, client DriverStore path `C:\Windows\System32\DriverStore\FileRepository\cnlb0ma64.inf_amd64_d4f4062dad259878`.
- Point-and-Print policy key: `HKLM\SOFTWARE\Policies\Microsoft\Windows NT\Printers` (+ `\PointAndPrint`).
## Update: 12:44 PT — more VLAN20 printers (dining/chef/medtech), workgroup machines, machine rename, GPO planning doc
Continued the VLAN 20 printer migration with several **workgroup** (not-domain-joined) machines, where the clean approach is a **direct-IP local printer installed as SYSTEM (machine-wide / all users)** — no `\\CS-SERVER` share, no domain auth, no point-and-print prompt.
- **Dining Room Manager** (DESKTOP-MD6UQI3, WORKGROUP, user `dining manager` / Alyssa): set up direct-IP `Dining Room Manager - Canon MF743CDW` -> 10.0.20.228 (UFR II driver `Canon Generic Plus UFR II V250`, already present), machine-wide, default. Removed stale `Kitchen Admin` USB printer (per Howard). Initially misnamed it "Kitchen -" -> renamed to "Dining Room Manager -" (Howard: the kitchen printer is a separate device with the chefs; this is her own-room printer).
- **Chef's Office** (CHEF-PC, WORKGROUP): set up direct-IP `Chef Office - Brother MFC-9330CDW` -> 10.0.20.236 (driver `Brother MFC-9330CDW Printer`), machine-wide / all users (Howard: machine still needs to be domain-joined). LEFT the existing USB `Chef Printer` in place and LEFT it as the chef's default — verified per-user defaults via registry: real user `JD Martin` still defaults to USB `Chef Printer` (the "default = Chef Office" seen post-install was only SYSTEM's context, not a user). So JD Martin = chef on CHEF-PC.
- **Memory Care front desk** (MEMRECEPT-PC, WORKGROUP, user `memfrtdesk`): identified + marked in the GPO doc. Printer = `MCReception` Epson ET-5800 @ 10.0.20.78. Currently only has the Epson via OLD vendor/WSD ports (`EP833571:ET-5800 SERIES`), not the static .78. NOT set up yet (Howard asked to mark it down for the GPO; setup TBD).
- **Memory Care MedTech Brother MFC-L8900CDW @ 10.0.20.74** set up direct-IP machine-wide on BOTH the memcare box and DESKTOP-LPOPV30 (user `karen rossini`); removed the OLD connections on the retired IP 192.168.2.53 (+ a stale WSD `Med Tech` on the memcare box). On LPOPV30 the old one was Karen's default -> set the NEW printer as her default. Memcare box default left as iR-ADV.
- **Machine rename:** RECEPTIONIST-PC (the Memory Care box, "memory care" user, S/N MJ0KQH4R, agent 57f19e17) shares its hostname with the front-desk RECEPTIONIST-PC box and was hard to tell apart -> `Rename-Computer -NewName MEMCARE-STATION -Force` STAGED (applies on next reboot; not forced, user active). The OTHER RECEPTIONIST-PC (frontdesk user, S/N MJ0KQHNP) is the real front desk.
- **GPO planning doc created:** `clients/cascades-tucson/docs/printer-gpo-map.md` — running printer->machine->user->IP->driver map for the eventual printer GPO, including the required Point-and-Print policy layer, the per-room/per-user assignments, the UFR II driver requirement for Canon MF741/743, and TODOs (add to GPO + map to domain accounts once the workgroup machines are domain-joined; repoint `CSC - Life Enrichment Printers` GPO to the new share).
**Decisions/notes this update:**
- Workgroup machines get direct-IP printers now; switch to GPO-deployed `\\CS-SERVER\<share>` once domain-joined.
- A SYSTEM-context `Get-CimInstance Win32_Printer .Default` reports SYSTEM's default, NOT the logged-on user's — read per-user defaults from `HKU\<sid>\...\Windows\Device` to see the real user default.
- Building the direct-IP printers used `Add-PrinterPort`/`Add-Printer` (SYSTEM) so they're machine-wide; per-user default changes done via `WScript.Network.SetDefaultPrinter` in the user session.
**Problem:** on the memcare box the spooler went unreachable (`0x800706ba`) right at the test-print step after add+remove; recovered with `Restart-Service Spooler -Force`, re-verified the new printer (Normal) and re-sent the test.
**Pending after this update:**
- Reboot MEMCARE-STATION (memcare box) when convenient to apply the rename.
- Set up the Memory Care front-desk Epson ET-5800 (10.0.20.78) on MEMRECEPT-PC if desired (currently only marked down).
- Confirm dining/chef/medtech test pages printed (Howard verifying at the printers).
- All workgroup machines (DESKTOP-MD6UQI3, CHEF-PC, MEMCARE-STATION, MEMRECEPT-PC, DESKTOP-LPOPV30): domain-join + GPO migration still pending.
New IPs/printers this update: Dining Canon MF743CDW 10.0.20.228; Chef Brother MFC-9330CDW 10.0.20.236; MedTech Brother MFC-L8900CDW 10.0.20.74; MemCare front desk Epson ET-5800 10.0.20.78 (not yet set up).

View File

@@ -17,7 +17,17 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
<!-- Append entries below this line -->
2026-06-30 | Mikes-MacBook-Air.local | birthbiologic/quality-sync | [correction] Failed to complete Quality Dept sync yesterday as promised; task was to make SharePoint QualitySystemsDepartment match Datto exactly by deleting files not in Datto. Misunderstood task - merged SharePoint sites instead of syncing to Datto. Corrected today by deleting 5 files not in Datto source.
2026-06-30 | Howard-Home | remediation-tool/exchange-op | [friction] Add-MailboxPermission -AutoMapping $true silently rolled back the FullAccess grant for 2 of 4 delegates (cmdlet echoed [FullAccess] success but Get-MailboxPermission showed NONE); a failed msExchDelegateListLink write aborts the whole Add transaction. Fix: re-add with -AutoMapping $false (FullAccess then persists); set automapping separately/interactively if auto-attach is required. [ctx: tenant=cascadestucson.com mailbox=tamra.matthews app=ComputerGuru-Exchange-Operator]
2026-06-30 | Howard-Home | /syncro | [correction] invoiced 'Windows Pro Upgrade' line items (Cascades 67887/67890) with blank CATEGORY; product_category was null and I billed it anyway — correct is to pre-flight GET /products/<id>, never invoice a null/blank category, and never invent one (use existing set e.g. Software) [ctx: ref=feedback_syncro_line_item_category invoices=67887,67890 product=23571919]
2026-06-30 | Howard-Home | rmm/printer-map | [friction] Add-Printer -ConnectionName in user_session = HRESULT 0x800702e4 ELEVATION_REQUIRED (Point-and-Print); agent watchdog times out on the interactive UAC prompt. Use WScript.Network.AddWindowsPrinterConnection + have a user at console approve, or pre-stage driver/connection as SYSTEM
2026-06-30 | Howard-Home | rmm/powershell | [friction] literal UNC backslashes (hostshare) in a jq-built PS payload got mangled to a single backslash -> Add-Printer 'invalid name'; fix: build UNC with [char]92 instead of literal backslashes [ctx: ref=feedback_windows_quote_stripping host=RECEPTIONIST-PC]
2026-06-30 | Howard-Home | unifi-wifi/pfsense-ssh | SSH connect/auth failed (rc=255) [ctx: host=192.168.0.1:22 slug=cascades-tucson act=fw-list]
2026-06-30 | Howard-Home | unifi-wifi/pfsense-ssh | SSH connect/auth failed (rc=255) [ctx: host=192.168.0.1:22 slug=cascades-tucson act=fw-list]
2026-06-30 | Howard-Home | unifi-wifi/pfsense-ssh | SSH connect/auth failed (rc=255) [ctx: host=192.168.0.1:22 slug=cascades-tucson act=audit]
@@ -140,6 +150,7 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
2026-06-26 | Howard-Home | rmm/bash-quoting | [friction] doubled single-quotes ('') inside a single-quoted bash $SCRIPT collapse the string, leaving a PowerShell registry path with a space unquoted (Windows NT) -> read fails, ErrorActionPreference=Stop aborts before changepk. Fix: use double-quotes for paths inside the single-quoted bash heredoc, never doubled single-quotes. [ctx: ref=feedback_windows_quote_stripping]
2026-06-25 | GURU-5070 | vault/display | [friction] echoing a vault entry, sed line-redaction missed the multi-line JSON private_key (matched 'key:' not 'private_key": "') and printed the full SA private key; when displaying vault entries use vault.sh get-field for named fields or drop the entire credentials: block, never a line-regex over JSON credential blobs
2026-06-29 | GURU-BEAST-ROG | mailprotector | HTTP 404 POST https://emailservice.io/api/v1/users/find_by_address: "Not found" [ctx: cmd=find-user]
2026-06-26 | GURU-BEAST-ROG | email-investigation | [correction] assumed tedards.net also uses GuruProtect/Inky; correct: only ACG uses Inky for inbound. Tedards routes directly to Exchange Online.

View File

@@ -0,0 +1,57 @@
## User
- **User:** Howard Enos (howard)
- **Machine:** Howard-Home
- **Role:** tech
## Session Summary
Howard flagged a Syncro billing defect on his invoice list (invoice 1650806091 view): "Windows Pro Upgrade" line items on Cascades of Tucson tickets #32466 and #32474 (invoices 67887 and 67890) went out with a blank CATEGORY column, while labor lines correctly showed "Labor". The directive was explicit — do NOT fix the ticket (Winter already corrected the records); fix the skill so I stop making this mistake, since categories are a fixed set in Syncro and must not be invented or left blank.
Investigated the root cause read-only via the Syncro REST API. Located the product "Windows Pro Upgrade" (id 23571919) and confirmed both `category` and `product_category` are `null` on the product itself — that null propagates to every line item billed from it, producing the blank CATEGORY column. Confirmed `product_category` is the operative field (the field that shows "Labor" on labor products like 1190473). Enumerated the controlled vocabulary by paging all ~960 products: real categories in the tenant are Labor, Default, Subscriptions, Product, Service/Services, Other, Contract, Discount, Exempt Labor, Prepay Hours, Deposit, Hardware (plus Software per Howard's set selections). The `/product_categories` API endpoint returned empty, so the live set must be derived by enumerating distinct `product_category` across `GET /products`.
Fixed the skill rules only (no ticket/product writes). Added a controlled-vocabulary rule and a pre-flight check to `.claude/commands/syncro.md`, created a `feedback` memory with an index line, and logged the correction to `errorlog.md`. The net behavioral change: before invoicing, the `/syncro` flow must `GET /products/<id>`, verify `product_category` is a real non-null value, halt on a blank one, and never substitute an invented category.
## Key Decisions
- Fixed the skill at two layers: the bolded rules block (next to the `taxable` rule) AND the `add_line_item` required-fields procedure, so the check sits in the workflow, not just the header.
- Did not hardcode the category list as authoritative — instructed the skill to enumerate the live set from `GET /products` (it changes), with known-good values listed only as a reference and "ASK if none fits."
- Targeted the line-item/invoicing path (where I act) rather than product creation — the skill does not create products, and the product-level fix is Winter's record correction.
- Wrote a `feedback`-type memory (shapes future behavior fleet-wide) in addition to the errorlog correction (pattern-linting), per the complementary-logging rule.
## Problems Encountered
- `/product_categories` API endpoint returned empty — resolved by enumerating distinct `product_category` across all paged `GET /products` to recover the controlled vocabulary.
- Product exposes several category-ish fields (`category`, `product_category`, `category_id`, `category_path`) — confirmed `product_category`/`category_path` are the populated ones (both "Labor" on a labor product) and `product_category` is the canonical field to check.
## Configuration Changes
- Modified: `.claude/commands/syncro.md` — added controlled-vocabulary + no-blank-category rule (after the taxable rule); added category pre-flight to the `add_line_item` required-fields list.
- Created: `.claude/memory/feedback_syncro_line_item_category.md` — feedback memory for the rule.
- Modified: `.claude/memory/MEMORY.md` — added index line for the new memory.
- Modified: `errorlog.md` — logged the correction (--correction).
## Credentials & Secrets
None created or discovered. Used the per-user Syncro API key already baked into `.claude/commands/syncro.md` (howard token) for read-only GETs.
## Infrastructure & Servers
- Syncro PSA: `https://computerguru.syncromsp.com/api/v1` — query-param auth (`?api_key=`).
## Commands & Outputs
- `GET /products?query=Windows%20Pro%20Upgrade` → product id 23571919, `product_category: null`, `price_retail: 99.0`, `taxable: true`.
- `GET /products/1190473` (Labor - Remote Business) → `product_category: "Labor"` (confirms the field that renders the CATEGORY column).
- Paged enumeration of all products → distinct `product_category`: Labor, Default, Subscriptions, Product, Service(s), Other, Contract, Discount, Exempt Labor, Prepay Hours, Deposit, Hardware (+ Software per Syncro admin set).
- `bash .claude/scripts/log-skill-error.sh "/syncro" "..." --correction``[OK] logged skill error to errorlog.md`.
## Pending / Incomplete Tasks
- None for the skill fix. (Ticket/product record correction is Winter's, already done — invoices 67887/67890, product 23571919.)
- Optional future: the product 23571919 `product_category` should be set to "Software" at the product level so future bills inherit it — outside this session's scope (Winter handling records).
## Reference Information
- Tickets: Cascades of Tucson #32466, #32474. Invoices: 67887, 67890. Invoice list view: invoice 1650806091.
- Product: "Windows Pro Upgrade" id 23571919.
- Files: `.claude/commands/syncro.md`, `.claude/memory/feedback_syncro_line_item_category.md`, `.claude/memory/MEMORY.md`, `errorlog.md`.

View File

@@ -0,0 +1,73 @@
## User
- **User:** Mike Swanson (mike)
- **Machine:** GURU-BEAST-ROG
- **Role:** admin
## Session Summary
Short maintenance span following the 2026-06-19 re-clone-recovery / VWP-time-sync session
(logged in `2026-06-19-mike-reclone-recovery-and-vwp-time-sync.md`). Across several routine
`/sync` runs the `projects/discord-bot` submodule kept being reported by `sync.sh` as having
uncommitted changes, which prompted a closer look.
Root cause: the recovered Python virtualenv lives at `.venv/` (the leading-dot convention from
`python -m venv .venv`), but the submodule's `.gitignore` only ignored `venv/` — so `.venv/`
showed as untracked and every parent sync flagged the submodule dirty. There were no actual bot
code changes; the prior code fix (`0487b15`) was already committed and pushed. Fixed by adding
`.venv/` to `projects/discord-bot/.gitignore`, committed in the submodule on `main`
(`2c91a2a`) and pushed to the acg-discord-bot Gitea repo. Subsequent `/sync` confirmed the
submodule is now reported clean (`[branch main]`, not "uncommitted changes").
Also during this span, routine syncs pulled fleet work (gitea/synology/screenconnect/datto-edr
skills, Bitdefender + GuruRMM buildout, Khalsa/Jimmy/Four Paws client work) and the vault picked
up several new client/infra credentials. The vault repo's git identity was found set to Howard
Enos on this machine and was auto-corrected to Mike Swanson by `sync.sh`'s identity reconcile
before committing a local Four Paws credential. The CRITICAL harness-guard macOS-bash-3.2 fix
(`4c51be5`) was pulled and self-check confirmed GREEN (PASS 83/0/0); census published.
## Key Decisions
- **Fix the gitignore gap, never commit the venv.** The "uncommitted changes" was an untracked
`.venv/`, not real work. Correct fix is ignoring `.venv/` (a venv must never be committed), not
committing or force-cleaning it.
- **Commit in the submodule's own history, not the parent.** The `.gitignore` belongs to the
discord-bot repo; committed there on `main` and pushed. Parent gitlink intentionally left
unadvanced (sync's default; `--with-submodules` would be needed to bump the pin).
## Configuration Changes
- Modified: `projects/discord-bot/.gitignore` — added `.venv/` under the virtualenv block.
Submodule commit `2c91a2a` on `main`, pushed `0487b15..2c91a2a` to acg-discord-bot.git.
## Credentials & Secrets
- None created/discovered this span. Vault auto-committed a pre-existing local
`clients/four-paws/localadmin.sops.yaml` and pulled new entries (cascades-tucson/meredith-kuhn,
four-paws/staff-workgroup, infrastructure/windows-pro-mak) from the fleet — all via vault sync,
none handled manually here.
## Infrastructure & Servers
- discord-bot submodule origin: `git.azcomputerguru.com/azcomputerguru/acg-discord-bot.git`.
- Bot service: `ClaudeToolsDiscordBot` (nssm), runs from `projects/discord-bot/.venv` on GURU-BEAST-ROG.
## Commands & Outputs
- `git -C projects/discord-bot check-ignore .venv` -> not ignored (before) / `.venv` (after fix).
- Submodule was detached HEAD at origin/main; `git checkout -B main origin/main` then commit/push.
- Post-fix `/sync`: `sync: leaving submodule with local work untouched: projects/discord-bot [branch main]`
(previously reported `[uncommitted changes]`).
## Pending / Incomplete Tasks
- `ClaudeTools.old` deletion still pending Mike's go (fully drained + verified in the 2026-06-19
session; ~30GB, irreversible).
- VWP member time-sync converged (verified 2026-06-19/20); 3 then-offline members
(DESKTOP-S4GNL8O, VWP-ChrisG-L, VWP-JR-L) apply config on reconnect — no action unless a drive
issue recurs.
## Reference Information
- Submodule commit: `2c91a2a` (gitignore .venv). Prior bot code fix: `0487b15`.
- Harness-guard fix pulled: `4c51be5`. Self-check: GREEN, census `selfcheck_GURU-BEAST-ROG`.
- Prior session log: `session-logs/2026-06/2026-06-19-mike-reclone-recovery-and-vwp-time-sync.md`.

View File

@@ -1,2 +1,3 @@
# Bot Activity Log - 2026-06-25
16:47 PT - Winter/Mike - Tedards #32228 agencyzoomify.com emails to trash - Applied TABL sender allow (permanent) on tedards.net; resolved Syncro ticket; sent outreach email to lindsay@agencyzoomify.com (CC y226@tedards.net) re Wirechunk DKIM fix
19:13 PT - Mike - Tedards agencyzoomify investigation (bt@tedards.net trace, Lindsay replies, GuruProtect analysis) - bot session closed; Mike continuing on ticket #5070