From ae2ee346b5a19152cc9445d393a33a774ab61efc Mon Sep 17 00:00:00 2001 From: Howard Enos Date: Fri, 10 Jul 2026 17:09:47 -0700 Subject: [PATCH] syncro: Phase-2 deploy-agent + asset/RMM verified on a real device deploy-agent proven end-to-end on RMM-TEST-MACHINE: vaulted Howard Test MSI URL -> GuruRMM push (msiexec /qn, SYSTEM) -> Syncro service Running -> auto-enrolled as asset 12676769 under the client + the policy folder embedded in the installer token (5092561) in ~1 min. installed_applications populates fast; patches lag. POST /policy_folders {policy_folder} requires parent_id (null->422); move-asset flip-restore verified on the real asset; DELETE policy_folder works. Shapes + findings recorded. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/commands/syncro.md | 2 ++ .claude/standards/syncro/test-findings.md | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/.claude/commands/syncro.md b/.claude/commands/syncro.md index 8145d822..dd62f58f 100644 --- a/.claude/commands/syncro.md +++ b/.claude/commands/syncro.md @@ -596,6 +596,8 @@ Every endpoint's response shape, verified against the live API. Parse exactly as | Create PO | POST `/purchase_orders` | `{"purchase_order": {...}}` | `.purchase_order.id` — no DELETE | | Add PO line | POST `/purchase_orders/{id}/create_po_line_item` | `{"po_line_item": {...}}` | needs product `maintain_stock:true` | | Create worksheet | POST `/tickets/{id}/worksheet_results` | `{"worksheet_result": {...}}` | `.worksheet_result.id` — template ids in `GET /tickets/settings` | +| Create policy folder | POST `/policy_folders` | `{"policy_folder": {...}}` | `.policy_folder.id` — `parent_id` REQUIRED (null → 422); full-scope token | +| Delete policy folder | DELETE `/policy_folders/{id}` | empty body / 200 | full-scope token only | **Invoice GET line_items field names differ from ticket line_items:** `item` = product name, `name` = description, `price` = unit rate. Do not use `price_retail` when reading invoice line items. diff --git a/.claude/standards/syncro/test-findings.md b/.claude/standards/syncro/test-findings.md index 250afc61..b567993f 100644 --- a/.claude/standards/syncro/test-findings.md +++ b/.claude/standards/syncro/test-findings.md @@ -5,6 +5,25 @@ Verified behaviors/gotchas discovered while testing `/syncro` against the live t Each entry is a rule the skill must not re-learn. Critical ones are also folded into `syncro.md` (operating manual) and `api-reference.md` (breadth reference). +## 2026-07-10 — Phase 2 (deploy-agent + asset/RMM on a REAL device) + +**`deploy-agent` VERIFIED end-to-end on RMM-TEST-MACHINE.** Read the vaulted Howard Test MSI URL +→ pushed via GuruRMM (`msiexec /i "" /qn /norestart`, SYSTEM context, via `ps-encoded.sh`) +→ `msiexec` exit 0, service **`Syncro` Running** → the machine **auto-enrolled as a Syncro asset +under the correct client + the policy folder embedded in the installer token** (`5092561`) within +~1 min. Asset `12676769`. Downloaded ~7.3 MB MSI. This is the whole reason the installer URL is +per-policy-folder: the token binds the enrollment to that client + folder. + +- **`installed_applications` populates within ~1 min** of enroll; **`patches` lag** (scan + pending → `0/0` immediately after enroll). Both endpoints work; patch data just isn't instant. +- **`POST /policy_folders` → `{"policy_folder":{...}}`** (`.policy_folder.id`). **`parent_id` is + REQUIRED** — `null` → `{"errors":["Parent is required"]}`. Create a client subfolder with + `parent_id` = the client's top-level folder id. `DELETE /policy_folders/{id}` works (empty body). +- **`move-asset` (PUT `policy_folder_id`) VERIFIED on the real asset**: `5092561 → 5092583 → + 5092561`, both confirmed by GET. Needs a full-scope token (howard/winter have it). +- **`GET /rmm_alerts` works.** `ps-encoded.sh`/PowerShell returns CLIXML progress records on + stderr even on success — `exit_code == 0` is the truth; ignore the CLIXML noise. + ## 2026-07-10 — Phase 2 prep: RMM installer URL (structure decoded) **The Syncro RMM installer URL is a base64 token that partly encodes API-derivable IDs.**