feat: implement agent-os standards system and feature planning tools
- Split CODING_GUIDELINES.md into 19 indexed standards files under .claude/standards/ - 9 from CODING_GUIDELINES (conventions, powershell, security, api, git, gururmm) - 10 from session log tribal knowledge (syncro, ssh, gitea, python, client, gururmm) - Add .claude/standards/index.yml for cheap relevance-based lookup - Add /inject-standards command: load targeted standards per task instead of full guidelines - Add /shape-spec command: pre-implementation spec for GuruRMM features (plan.md, shape.md, references.md, standards.md) with mandatory out-of-scope gate - Add docs/tech-stack.md and docs/mission.md for ClaudeTools API - Add projects/msp-tools/guru-rmm/docs/tech-stack.md and mission.md for GuruRMM - Update CLAUDE.md commands table with /inject-standards and /shape-spec Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
60
.claude/standards/syncro/comment-dedup.md
Normal file
60
.claude/standards/syncro/comment-dedup.md
Normal file
@@ -0,0 +1,60 @@
|
||||
---
|
||||
name: comment-dedup
|
||||
description: Never retry POST /comment without first GET /tickets/{id} to confirm it didn't land; always use heredoc payloads
|
||||
applies-to: syncro, all
|
||||
---
|
||||
|
||||
# Syncro Comment Deduplication
|
||||
|
||||
## The rule
|
||||
|
||||
Never retry a POST `/comment` call without first doing a `GET /tickets/{id}` to confirm the first attempt did not land.
|
||||
|
||||
Syncro has no DELETE endpoint for comments. A duplicate comment cannot be removed via the API — only manually through the Syncro GUI by an admin. Duplicate comments are visible to clients (public comments) and to all technicians (hidden comments), and the content cannot be redacted without deleting the entire comment.
|
||||
|
||||
## Incident that established this rule
|
||||
|
||||
2026-05-01: A comment with completely wrong content ("Karen Rossini" and the ALDOCS Cascades share) was posted to a Sombra Residential ticket. Root cause: a `/tmp` path mismatch on Windows caused curl to read a stale payload from a previous session rather than the newly written one. The wrong comment could not be deleted via API — Howard had to manually delete it through the Syncro GUI.
|
||||
|
||||
## Required pattern
|
||||
|
||||
**Before retrying any failed comment POST:**
|
||||
|
||||
```bash
|
||||
# Check if the comment landed before retrying
|
||||
curl -s "https://computerguru.syncromsp.com/api/v1/tickets/${TICKET_ID}?api_key=${API_KEY}" \
|
||||
| jq '.ticket.comments[-1]'
|
||||
```
|
||||
|
||||
If the comment is there, do not retry. If it is missing, diagnose why before retrying (network failure, wrong API key, wrong ticket ID).
|
||||
|
||||
## Payload pattern — use heredocs, not temp files
|
||||
|
||||
Do not write comment payloads to `/tmp/` files and read them back with curl. On Windows, `/tmp` resolves differently in Git Bash vs. the Claude Code Write tool, causing stale-file reads.
|
||||
|
||||
**Correct — heredoc piped directly to curl:**
|
||||
|
||||
```bash
|
||||
curl -s -X POST "${BASE}/tickets/${TICKET_ID}/comment?api_key=${API_KEY}" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data-binary @- <<'JSON'
|
||||
{"subject": "Resolution", "body": "...", "hidden": false, "do_not_email": false}
|
||||
JSON
|
||||
```
|
||||
|
||||
Use `<<'JSON'` (single-quoted, literal) for static payloads. Use `<<JSON` (double-quoted, interpolating) when the payload includes shell variables like `${TIMER_ID}`.
|
||||
|
||||
**Wrong — temp file approach (prone to path mismatch):**
|
||||
|
||||
```bash
|
||||
# Write tool writes to C:\tmp\, Git Bash reads from %LOCALAPPDATA%\Temp\
|
||||
# These are different directories on Windows
|
||||
cat > /tmp/comment.json << 'EOF'
|
||||
{"subject": "Resolution", ...}
|
||||
EOF
|
||||
curl ... -d @/tmp/comment.json # May read wrong file
|
||||
```
|
||||
|
||||
## Applies also to
|
||||
|
||||
All Syncro POST endpoints, not just comments. Timer entries, line items, and invoices all have the same no-retry-without-check rule since duplicates are visible and often cannot be deleted via API.
|
||||
58
.claude/standards/syncro/html-formatting.md
Normal file
58
.claude/standards/syncro/html-formatting.md
Normal file
@@ -0,0 +1,58 @@
|
||||
---
|
||||
name: html-formatting
|
||||
description: Use <br> for line breaks in Syncro comments; <ul>/<li> collapses; no emojis
|
||||
applies-to: syncro
|
||||
---
|
||||
|
||||
# Syncro Comment HTML Formatting
|
||||
|
||||
## Line breaks
|
||||
|
||||
Use `<br>` for line breaks in Syncro ticket comment bodies. Do not use `\n` (literal newline) — Syncro renders comment bodies as HTML, and plain newlines are collapsed to a single space.
|
||||
|
||||
```json
|
||||
{
|
||||
"body": "Work performed:<br>- Replaced wireless adapter driver<br>- Verified network connectivity<br>- Customer running overnight memtest"
|
||||
}
|
||||
```
|
||||
|
||||
## Lists
|
||||
|
||||
`<ul>/<li>` does not render correctly in Syncro comments — it collapses into a single line with no visible separation. Use `<br>- ` as a bullet pattern instead:
|
||||
|
||||
**Wrong:**
|
||||
```json
|
||||
{"body": "<ul><li>Item one</li><li>Item two</li></ul>"}
|
||||
```
|
||||
|
||||
**Correct:**
|
||||
```json
|
||||
{"body": "Items completed:<br>- Item one<br>- Item two"}
|
||||
```
|
||||
|
||||
## No emojis
|
||||
|
||||
No emojis in Syncro comments. The same encoding rule that applies to all other output (see `conventions/no-emojis`) applies here. Syncro sends public comments as email; emoji characters can display incorrectly in various email clients.
|
||||
|
||||
Use `[OK]`, `[COMPLETE]`, `[PENDING]`, `[ACTION REQUIRED]` instead.
|
||||
|
||||
## Hidden vs. public comments
|
||||
|
||||
Set the visibility appropriately:
|
||||
|
||||
```json
|
||||
{
|
||||
"hidden": true,
|
||||
"do_not_email": true
|
||||
}
|
||||
```
|
||||
|
||||
- `hidden: true` — internal only; client does not see it, no email sent
|
||||
- `hidden: false` — client-visible; Syncro sends an email unless `do_not_email: true`
|
||||
- `do_not_email: true` — suppress the email notification even for public comments
|
||||
|
||||
If a resolution comment contains passwords, internal notes, or credentials: always use `hidden: true, do_not_email: true`.
|
||||
|
||||
## Cascades-specific note
|
||||
|
||||
Never set `contact_id` on Cascades of Tucson tickets. Leaving it blank lets Syncro route the email to the correct distribution. Setting it (Meredith Kuhn has been the incorrect default in past sessions) overrides the distribution and breaks notification routing.
|
||||
62
.claude/standards/syncro/time-entry-protocol.md
Normal file
62
.claude/standards/syncro/time-entry-protocol.md
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
name: time-entry-protocol
|
||||
description: Always use timer_entry flow for billing; ask minutes and labor type before logging any time; never assume defaults
|
||||
applies-to: syncro
|
||||
---
|
||||
|
||||
# Syncro Time Entry Protocol
|
||||
|
||||
## Always ask before logging time
|
||||
|
||||
Before logging any time entry, ask the user:
|
||||
1. How many minutes?
|
||||
2. What labor type? (onsite, remote, emergency, warranty, project, etc.)
|
||||
|
||||
Never assume a default. Never round up or fill in a number. Billing errors are client-facing, hard to reverse, and affect prepaid block balances. An incorrect time entry requires Winter (billing) to manually reverse it.
|
||||
|
||||
## The required flow
|
||||
|
||||
All time-bearing work must use `timer_entry → charge_timer_entry`, not bare `add_line_item`. This is a hard rule.
|
||||
|
||||
```
|
||||
1. POST /tickets/{id}/timer_entry — create the time record
|
||||
2. POST /tickets/{id}/charge_timer_entry — generate the line item from the timer
|
||||
3. Verify line item: GET /tickets/{id} → check price_retail on the new line item
|
||||
4. If price_retail is wrong: PUT /tickets/{id}/line_items/{item_id} to patch it
|
||||
5. POST /invoices — roll line item onto invoice
|
||||
6. PUT /tickets/{id} — set status to Invoiced
|
||||
```
|
||||
|
||||
The `add_line_item` endpoint bypasses Syncro's time-tracking table entirely. Using it for labor means hours appear in the invoice but not in time-tracking reports (hours per client, technician productivity, average resolution time, prepay burn rate). After the 2026-04-30 audit, 31 closed tickets had 00:00:00 in time tracking because bare `add_line_item` was used for all of them.
|
||||
|
||||
## When bare add_line_item is acceptable
|
||||
|
||||
Only for non-time items:
|
||||
- Hardware/parts
|
||||
- Flat-fee services with no labor component
|
||||
- Software licenses
|
||||
|
||||
Even warranty or free labor must use `timer_entry` with `billable: false`. The only exception is cancelled tickets where no work was performed.
|
||||
|
||||
## Labor type reference
|
||||
|
||||
| Situation | Product | Note |
|
||||
|-----------|---------|-------|
|
||||
| Standard onsite | `26118` Onsite Business | At `hours × $175` |
|
||||
| Emergency/after-hours | `26184` Emergency or After Hours | Full rate, no quantity multiplier |
|
||||
| Prepaid project labor | `9269129` Prepaid Project Labor | At `$0/hr`; debits from prepay block |
|
||||
| Warranty | Any labor product | `billable: false` on timer_entry |
|
||||
|
||||
## Prepaid customers
|
||||
|
||||
Before applying any rate, verify `prepay_hours` on the customer record:
|
||||
```bash
|
||||
curl -s "https://computerguru.syncromsp.com/api/v1/customers/${CUSTOMER_ID}?api_key=${API_KEY}" \
|
||||
| jq '.customer.prepay_hours'
|
||||
```
|
||||
|
||||
If `prepay_hours > 0`, use the prepaid product at `$0/hr` and verify the balance debits correctly after the invoice posts (Syncro may not debit until the invoice is paid in the GUI — flag for Winter if uncertain).
|
||||
|
||||
## Note on billable: false
|
||||
|
||||
The Syncro API ignores `billable: false` on `timer_entry` calls silently — the entry is created but the billing flag has no effect through the API. If a warranty/free entry is needed, create the timer entry, then verify through the GUI that the line item generated by `charge_timer_entry` is at $0. Patch with `update_line_item` if it came in at a non-zero rate.
|
||||
Reference in New Issue
Block a user