syncro: capture Phase-1 live-test findings (email-delivery trap + response shapes)
Test campaign on Howard Test sandbox (customer 36118743) surfaced traps the skill must not
re-learn:
- invoice/estimate email endpoints return 200 "Email sent" even with NO recipient
(customer.email null) -> never report delivery from the 200; verify customer.email first.
- customer.email is UNIQUE tenant-wide; PUT /customers failure returns
{success:false,message:[...]} not {customer:{...}} -> check .success.
- POST/PUT /contacts return a FLAT object (.id), not {contact:{...}}; contact recipient
flags (primary/receives_invoices) are ignored via API.
- POST /contracts write succeeds but body doesn't echo the object; /contracts?customer_id
doesn't filter server-side -> GET-verify + client-filter.
New living log .claude/standards/syncro/test-findings.md; critical items folded into
syncro.md Hard Rules + Verified Response Shapes. Correction logged to errorlog.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -92,6 +92,21 @@ $PYTHON -c "import datetime; d = datetime.date(YYYY, M, D); print(d.strftime('%A
|
||||
|
||||
If any check fails, complete the missing step before reporting done. This rule fires on initial estimate creation AND on every subsequent "add X to the estimate" request. Incident: 2026-05-22, UPS added to estimate #7189 without a ticket note — caught by Winter.
|
||||
|
||||
**Emailing an invoice/estimate returns 200 "Email sent" even when it reached NOBODY — never
|
||||
report delivery from the 200 alone.** `POST /invoices/{id}/email` and `/estimates/{id}/email`
|
||||
take no recipient param; Syncro picks it from the record. If `customer.email` is null (and no
|
||||
recipient contact is flagged), the send is a silent no-op that still returns HTTP 200. Before
|
||||
emailing, confirm the customer has a valid `email`. Note `customer.email` is **UNIQUE
|
||||
tenant-wide** — reusing one returns `{"success":false,"message":["Email has already been
|
||||
taken"]}`; a `PUT /customers` that fails returns that error shape (not `{"customer":{...}}`),
|
||||
so check `.success` on customer writes instead of assuming a silent no-op.
|
||||
|
||||
**Living test-findings log:** `.claude/standards/syncro/test-findings.md` records verified
|
||||
behaviors/gotchas discovered while exercising the skill on the Howard Test sandbox
|
||||
(customer 36118743). Consult + append to it as new API quirks surface — it is how the skill
|
||||
avoids re-learning the same trap. Critical entries are folded into these Hard Rules and the
|
||||
Verified Response Shapes table.
|
||||
|
||||
## Implementation
|
||||
|
||||
When invoked, use the Syncro REST API via `curl`. All requests include `?api_key=<key>` as query parameter (NOT in header — Syncro uses query param auth).
|
||||
@@ -568,6 +583,13 @@ Every endpoint's response shape, verified against the live API. Parse exactly as
|
||||
| Add schedule line | POST `/schedules/{id}/line_items` | `{"schedule_line_item": {...}}` | `.schedule_line_item.id` |
|
||||
| Update schedule line | PUT `/schedules/{id}/line_items/{li_id}` | `{"schedule_line_item": {...}}` | `.schedule_line_item.quantity`, `.schedule_line_item.price_retail` |
|
||||
| Delete schedule line | DELETE `/schedules/{id}/line_items/{li_id}` | `{"success": true}` | — |
|
||||
| Create customer | POST `/customers` | `{"customer": {...}}` | `.customer.id` |
|
||||
| Update customer | PUT `/customers/{id}` | **success** `{"customer": {...}}` / **error** `{"success": false, "message": [...]}` | check `.success` — email is unique tenant-wide |
|
||||
| Create contact | POST `/contacts` | **FLAT** `{"id": N, "email": ...}` (NOT wrapped) | `.id` — GET-verify; null-parse ≠ failure |
|
||||
| Update contact | PUT `/contacts/{id}` | **FLAT** `{"id": N, ...}` | `.id` — `primary`/`receives_invoices` flags are ignored via API |
|
||||
| Create contract | POST `/contracts` | write OK but body does NOT echo the object | GET `/contracts` + match; never retry on null |
|
||||
| Email invoice | POST `/invoices/{id}/email` | `{"message": "Email sent."}` | **200 even with NO recipient** — verify `customer.email` first |
|
||||
| Email estimate | POST `/estimates/{id}/email` | `{"message": "Email sent"}` | **200 even with NO recipient** — verify `customer.email` first |
|
||||
|
||||
**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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user