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

## Session Summary

Expanded and end-to-end tested the `/syncro` skill from PSA-only to full Syncro API coverage,
including asset/RMM management and a working Syncro-agent deployment path. Started by auditing
the live OpenAPI 3.0 spec (`api-docs.syncromsp.com/swagger.json`) against the existing skill and
discovered the skill deliberately avoided asset writes based on a memory
(`reference-syncro-rmm-api-gui-only`) that turned out to be a token-scope symptom, not an API
limit. Pulled the complete inventory (~180 operations across 38 resource types), built a new
verified reference doc, and probed the live tenant with Howard's per-user token to map exactly
what works vs. what needs scope.

Corrected the record: the entire asset/RMM surface (create/update, patches,
installed_applications, rmm_alerts, contacts, contracts, leads, vendors, POs, products,
payments, wiki) is API-drivable; only policy-folder operations were 401-gated by a missing
"Assets - Policy Change" token scope. Mike upgraded Howard's token to full scope mid-session;
verified the policy-folder move works via flip-and-restore on a test asset. Also checked Mike's
and Winter's keys — Winter full, Mike's vaulted token (…ebbeb3) still 401 (needs re-vault).

Ran a full live test campaign on a new sandbox customer ("Howard Test", 36118743). Phase 1
exercised the complete PSA + breadth write surface (tickets, comments, line items, invoices,
estimates, appointments, schedules, contracts, contacts, leads, vendors, products, purchase
orders, worksheets) and captured every response shape and gotcha. Phase 2 proved the deploy
path end-to-end: pushed the per-policy-folder Syncro MSI installer to RMM-TEST-MACHINE via
GuruRMM (`/rmm`), the box auto-enrolled as a Syncro asset in the correct client + policy folder,
then tested patches, installed_applications, policy-folder create/move/delete, and rmm_alerts on
the real device.

Decoded the installer URL structure and confirmed it is a per-client secret (not constructible),
so harvested links are vaulted per client (4 seeded). Confirmed Syncro's public API has NO
remote device-control/exec endpoint — execution stays on GuruRMM. Finished by cleaning up the
sandbox (API-deleted the deletable records, uninstalled the Syncro agent from RMM-TEST-MACHINE)
and recording all findings in a living `test-findings.md` plus the skill's hard rules.

## Key Decisions

- Kept the high-frequency PSA workflows in `syncro.md` and put the full breadth in a new
  `api-reference.md` rather than bloating the incident-scarred core.
- Installer-push (`deploy-agent`) lives in `/syncro` (Howard's call) but uses GuruRMM as the
  execution arm — Syncro has no installer-download or exec API.
- Chose lazy harvest of installer URLs (grab+vault+wiki-pointer at first deploy) over proactive
  harvesting of all ~69 clients.
- Vaulted installer URLs (they embed an unguessable per-client secret token) rather than storing
  plaintext in the wiki; wiki gets a pointer.
- Built `move-asset` with a capability preflight + mandatory post-write verify because an
  under-scoped token returns HTTP 200 and silently no-ops the folder change.
- Did not probe undocumented endpoints (e.g. guessed exec routes) per the skill's hard rule.

## Problems Encountered

- **Emails not delivered despite 200 "Email sent".** Invoice/estimate email endpoints take no
  recipient param and send to nobody when `customer.email` is null. `customer.email` is unique
  tenant-wide (Howard's real email was "already taken"). Fixed with a plus-addressed unique
  email (`howard+synctest@`), which delivered — resolved.
- **Null write responses looked like failures.** POST /contacts (flat `.id`), POST /contracts
  (no echo) returned null on the wrapped parse though the writes succeeded. GET-verify caught it;
  a blind retry would have duplicated. Documented the three response-shape families.
- **Policy-folder 401 then 404.** Old token 401'd on `/policy_folders`; upgraded token 404s on
  the bare route but 200s with `?customer_id=N`. `POST /policy_folders` requires `parent_id`.
- **PO line item rejected** until the product had `maintain_stock:true`.
- **Customer would not hard-delete** — `DELETE /customers` disables instead when associated data
  (asset/lead) exists. Expected; noted GUI-only leftovers.
- **jq `--arg` path lookup quirk** on the swagger file — switched to single non-parameterized
  walks / hardcoded paths.
- **/tmp write blocked** by the block-tmp-path hook (working as intended) — used repo-relative
  `.claude/tmp/`.

## Configuration Changes

Created:
- `.claude/standards/syncro/api-reference.md` — full verified Syncro endpoint catalog + worked templates.
- `.claude/standards/syncro/test-findings.md` — living log of verified behaviors/gotchas.
- `session-logs/2026-07/2026-07-10-howard-syncro-skill-expansion.md` — this log.

Modified:
- `.claude/commands/syncro.md` — asset read/write/move, RMM alerts, deploy-agent, capability
  preflight helper, email-delivery hard rule, expanded Verified Response Shapes table, reference pointer.
- `.claude/memory/reference_syncro_rmm_api_gui_only.md` + `.claude/memory/MEMORY.md` — corrected
  the "RMM is GUI-only" belief to the verified token-scope reality.
- `errorlog.md` — two `--correction` entries (asset-API assumption; email 200 != delivered).

Vault (separate repo, pushed):
- `clients/{cascades-tucson,grabb-durando,reliant,howard-test}/syncro-agent-installer.sops.yaml`
  — Syncro RMM MSI installer URLs (encrypted).

## Credentials & Secrets

- Syncro per-user API keys (unchanged): `msp-tools/syncro` (Mike, uid 1735, …ebbeb3 — still
  lacks Policy-Change scope), `msp-tools/syncro-winter` (Winter, uid 1737, …5edb3a — full),
  `msp-tools/syncro-howard` (Howard, uid 1750, …680e18 — full, upgraded this session).
- Syncro installer URLs vaulted at `clients/<slug>/syncro-agent-installer` (`credentials.installer_url`).
  Structure: `base64(v1-{customer_id}-{A:secret}-48753-{policy_folder_id})`; `48753` = ACG Syncro
  account id; `A` is an unguessable per-client secret (not API-derivable).
- GuruRMM API creds unchanged: `infrastructure/gururmm-server.sops.yaml`.

## Infrastructure & Servers

- Syncro PSA API: `https://computerguru.syncromsp.com/api/v1` (query-param auth, 180 req/min).
- Syncro RMM installer host: `https://rmm.syncromsp.com/dl/msi/<token>` (Kabuto, separate from PSA API).
- GuruRMM: `http://172.16.3.30:3001` (JWT auth).
- RMM-TEST-MACHINE (GuruRMM agent id `99d6d692-99e0-4359-9f9c-f43be89f49e5`, online, v0.6.66,
  client "AZ Computer Guru", site "Howard-VM"). A second stale enrollment exists
  (`7d3456f5-…`, offline). Syncro agent MSI product code `{B7F56D3D-2AD3-4021-9D36-3B9E9C9FBE33}`.
- Sandbox: Syncro customer "Howard Test" 36118743 (now DISABLED), policy folder 5092561,
  enrolled asset 12676769.

## Commands & Outputs

- Decode installer token: `echo <b64> | base64 -d` → `v1-{customer_id}-{A}-48753-{policy_folder_id}`.
- Capability preflight (policy scope): `GET /policy_folders?customer_id=N` → 200 has scope, 401 lacks.
- Deploy (GuruRMM): download MSI, `Start-Process msiexec.exe -ArgumentList @('/i',$msi,'/qn','/norestart') -Wait`
  → service `Syncro` Running, asset enrolls in ~1 min.
- Uninstall (GuruRMM): find Uninstall reg entry `DisplayName -match 'Syncro'`, `msiexec /x {ProductCode} /qn /norestart`
  → service/process/dir/reg all removed.
- `DELETE /customers/{id}` on customer with associated data →
  `{"message":"Customer was disabled instead of deleted because they have associated data."}`.

## Pending / Incomplete Tasks

- **Mike's Syncro token (…ebbeb3) still lacks Assets-Policy-Change** — re-vault after he confirms/upgrades
  the correct token, so `move-asset` works in Mike-attributed sessions.
- **GUI-only sandbox leftovers** (no API DELETE): Howard Test asset 12676769 (archive), lead 43492446,
  vendor 809317, product 23639304, PO 2101221; customer 36118743 stays DISABLED until asset+lead cleared.
- Optional: add a `deploy-agent --uninstall` action (msiexec /x method verified).
- Optional: grant Policy-Change scope to all per-user tokens for consistent `move-asset`.
- Lazy-harvest installer URLs for other clients at first deploy.

## Reference Information

- Spec: `https://api-docs.syncromsp.com/swagger.json` (OpenAPI 3.0, 116 paths / ~180 ops / 38 tags).
- Skill: `.claude/commands/syncro.md`; reference `.claude/standards/syncro/api-reference.md`;
  findings `.claude/standards/syncro/test-findings.md`.
- Commits this session: `1f56098`, `12bb9f1`, `555e1ff`, `1fa1a42`, `ae2ee34`, `269449d`, `8afd7ec` (+ this save).
- Vault installer entries: `clients/{cascades-tucson,grabb-durando,reliant,howard-test}/syncro-agent-installer`.
