Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions library/sales-and-crm/zoho-desk/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
linters:
enable:
- errorlint
- govet
- ineffassign
- staticcheck
- unused
- bodyclose
- noctx
- rowserrcheck
- sqlclosecheck

formatters:
enable:
- gofmt
- goimports
51 changes: 51 additions & 0 deletions library/sales-and-crm/zoho-desk/.goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
version: 2
project_name: zoho-desk-pp-cli
changelog:
disable: true
builds:
- id: zoho-desk-pp-cli
main: ./cmd/zoho-desk-pp-cli
binary: zoho-desk-pp-cli
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X github.com/mvanhorn/printing-press-library/library/sales-and-crm/zoho-desk/internal/cli.version={{ .Version }}
targets:
- darwin_amd64
- darwin_arm64
- linux_amd64
- linux_arm64
- windows_amd64
- windows_arm64
- id: zoho-desk-pp-mcp
main: ./cmd/zoho-desk-pp-mcp
binary: zoho-desk-pp-mcp
env:
- CGO_ENABLED=0
ldflags:
- -s -w -X main.version={{ .Version }}
targets:
- darwin_amd64
- darwin_arm64
- linux_amd64
- linux_arm64
- windows_amd64
- windows_arm64
archives:
- formats: [tar.gz]
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
format_overrides:
- goos: windows
formats: [zip]
checksum:
name_template: checksums.txt
brews:
- name: zoho-desk-pp-cli
repository:
owner:
name: homebrew-tap
homepage: "https://github.com//zoho-desk-pp-cli"
description: "The only scriptable, agent-native CLI for Zoho Desk: every ticket operation plus a local SQLite store, offline search, and SLA/triage analytics no help-desk tool ships."
install: |
bin.install "zoho-desk-pp-cli"
bin.install "zoho-desk-pp-mcp"
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Zoho Desk CLI — Polish Result
Verify 100% (34/34), Scorecard 95/100, Dogfood FAIL->PASS, go vet 0, gosec(hand) 0, PII 7->0, tools-audit 0.
Fixes: wired orphaned `auth set-token` into auth tree (generator miss); added `pp:data-source local` to 8 novel commands; replaced example email with PII_EMAIL_EXAMPLE.
Retro candidates: auth set-token not auto-registered; gosec pin v2.21.4 won't compile under go1.26.4; 34 generated-file gosec findings; oauth2_refresh env-var wiring (found in Phase 5).
ship_recommendation: ship; further_polish_recommended: no.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Zoho Desk CLI — Shipcheck Proof

## Result: PASS (7/7 legs), Scorecard 95/100 Grade A, live-sample probe 8/8

| Leg | Result |
|-----|--------|
| verify | PASS |
| validate-narrative | PASS (10 commands resolved, full examples pass) |
| dogfood | PASS (novel_features 8/8 built) |
| workflow-verify | PASS |
| apify-audit | PASS |
| verify-skill | PASS (flags/commands/positional/sections) |
| scorecard | PASS 95/100 |

Scorecard highlights: Output Modes/Auth/Error/Terminal/README/Doctor/Agent-Native/Breadth/Vision/Workflows/Insight all 10. MCP Quality 8, Cache Freshness 5 (cache.enabled not set — read-through wrapper, acceptable), Type Fidelity 4/5, Agent Workflow 9. Dead code 5/5.

MCP: 52 tools, Cloudflare pattern (thin search+execute), readiness full.

## Phase 4.x agentic reviews
- 4.7 sync-param-drop: SKIPPED (hand-authored spec, no traffic-analysis).
- 4.8/4.9 docs correctness: 2 warnings fixed — triage "no-first-response"→"high-priority"; contact-360 recipe dropped "threads, and time" (command joins contact+account+tickets only). All commands/flags/examples resolve; Unique sections match novel_features_built (8); no placeholders/marketing smell.
- 4.85 output plausibility: PASS — all sampled commands emit valid JSON ([] not null) on empty store, no panics.
- 4.95 local code review: 1 HIGH fixed (doctor nil-deref panic on bad config), 1 LOW fixed (rebalance partial-apply now reports movesApplied+failures), 1 LOW accepted (env orgId may persist to config — non-secret, env wins each load).

## Behavioral verification (seeded store)
sla-radar excludes far-future / includes due-soon; triage ranks unassigned+overdue top; agent-load flags above-median; since respects window; contact-360 joins correctly; breach-history finds past-due; morning composes; rebalance proposes 0 on balanced load.

## Foundation correctness
- orgId injected into every request via Config.Headers (config.go Load); doctor surfaces missing orgId.
- Multi-DC via ZOHO_DESK_BASE_URL/ZOHO_DESK_TOKEN_URL.
- OAuth2 refresh-token auto-refresh.
- Reachability: 401 clean JSON, no bot protection.

## Verdict: ship (pending Phase 5 live dogfood — user setting up OAuth creds)
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# Printing Press Retro: Zoho Desk

## Session Stats
- API: zoho-desk (help-desk / ticketing)
- Spec source: hand-authored internal YAML (no official OpenAPI for Desk)
- Scorecard: 95/100 (A)
- Verify pass rate: 100% (34/34)
- Fix loops: shipcheck 1, polish 1
- Manual code edits: ~6 (orgId injection, doctor org check, 8 transcendence bodies, auth bridge fix, auth set-token registration via polish)
- Features built from scratch: 8 transcendence commands + shared helper

## Findings

### 1. oauth2_refresh custom env_vars never wired to the refresh client (Bug / Template gap)
- **What happened:** Spec declared `auth.type: oauth2_refresh` with `env_vars: [ZOHO_DESK_CLIENT_ID, ZOHO_DESK_CLIENT_SECRET, ZOHO_DESK_REFRESH_TOKEN]`. The generated `config.Load` reads those env vars into dedicated `ZohoDeskClientId/ZohoDeskClientSecret/ZohoDeskRefreshToken` struct fields. But `client.refreshAccessToken()` reads the GENERIC `cfg.ClientID/cfg.ClientSecret/cfg.RefreshToken` fields, and nothing bridges the two. Result: with fully valid credentials set via the documented env vars (or the `desk_*` config keys), every live call returns HTTP 401 and `doctor` reports "Auth: not configured." A manual refresh curl with the same creds succeeded, proving the creds were valid — the CLI just never fed them to the refresh path.
- **Scorer correct?** N/A — not a score penalty. Mock-mode verify/dogfood PASS because `PRINTING_PRESS_VERIFY` short-circuits the refresh. The bug is invisible to every gate except live dogfood, which is frequently skipped (auth-required APIs with no credential).
- **Root cause:** generator auth template. The config template emits BOTH a slug-named credential field set (from spec `env_vars`) AND the generic OAuth field set the refresh client consumes, with no assignment from the former to the latter in `Load`.
- **Cross-API check:** Recurs for every `oauth2_refresh` API whose spec carries custom `env_vars` (or whose canonical env var name differs from the generic default). The main SKILL's "Pre-Generation Auth Enrichment" actively instructs agents to set canonical env vars and `auth.canonical_env_var`, so this is the *encouraged* path, not an edge case.
- **Frequency:** every oauth2_refresh CLI with custom/canonical env var names.
- **Fallback if the Printing Press doesn't fix it:** The agent must (a) run live testing, (b) notice the 401, (c) trace config field vs refresh-client field mismatch, (d) hand-add a bridge in `config.go`. Steps a–c are easy to miss; many runs skip live testing entirely and ship a CLI that cannot authenticate.
- **Worth a Printing Press fix?** Yes. Silent, total live-auth failure on the auth path the SKILL recommends.
- **Inherent or fixable:** Fixable. In `config.Load`, after env + file parsing, fall back the generic `ClientID/ClientSecret/RefreshToken` from the slug-named fields when empty — OR have the refresh client read the slug-named fields when the generic ones are empty. Either bridges the gap for all such CLIs.
- **Durable fix:** Generator config template: when auth is `oauth2_refresh` and custom `env_vars` are declared, populate the generic OAuth credential fields the refresh client reads (e.g. `if cfg.ClientID == "" { cfg.ClientID = cfg.<Slug>ClientId }`, same for secret + refresh token) at the end of `Load`. Parameterized by the slug, not by `zoho`.
- **Test:** positive — generate an oauth2_refresh CLI with custom env_vars, set them, assert `doctor` shows "configured" and a live (or mock-live) call sends `Authorization`. negative — a CLI whose env_vars already ARE the generic names still authenticates (no double-assignment breakage).
- **Evidence:** doctor went "Auth: not configured" → "configured (oauth2 refresh)" after adding the 6-line bridge to `config.go` Load; live `organizations list` then returned real data.
- **Related prior retros:** None.

### 2. `auth set-token` command defined but never registered (Bug)
- **What happened:** `newAuthSetTokenCmd` exists and is referenced by `doctor` hints and `auth setup` guidance, but it was never `AddCommand`'d into the auth tree, so it was unreachable at runtime. dogfood flagged it as an unregistered command; polish wired it.
- **Scorer correct?** Yes — dogfood correctly detects the orphaned command. This is the reliable-fallback case: the scorer catches it every time, polish fixes it every time.
- **Root cause:** generator auth template emits the constructor but omits the registration call for this auth shape.
- **Cross-API check:** Same auth-template area as Finding 1; surfaced once here with direct evidence. Independently I can name only 1 API with evidence, so it does not clear the 3-API bar on its own.
- **Frequency:** auth-template-shape dependent; unverified beyond this run.
- **Fallback:** dogfood reliably catches "unregistered command" and polish fixes it — high-reliability fallback, low ship risk.
- **Worth a fix?** Cheap to fix alongside Finding 1 in the same auth template, but not independently urgent given the reliable scorer + polish fallback.
- **Durable fix:** register `newAuthSetTokenCmd` in the generated auth command tree whenever the constructor is emitted (fold into the Finding 1 auth-template work unit).
- **Test:** positive — generated token-auth CLI resolves `auth set-token --help` at exit 0. negative — auth types without a set-token concept don't emit a dangling command.
- **Evidence:** polish "wired the orphaned `auth set-token` command... never registered via AddCommand, so it was unreachable."
- **Related prior retros:** None.

### 3. Pinned gosec fallback (v2.21.4) won't compile under go1.26.4 (Recurring friction)
- **What happened:** polish's pinned gosec fallback `v2.21.4` fails to compile under go1.26.4 (stale `golang.org/x/tools` constant overflow). polish fell back to `gosec@latest`, which ran cleanly.
- **Scorer correct?** N/A — tooling, not scoring.
- **Root cause:** polish skill pins an older gosec that doesn't build on newer Go toolchains.
- **Cross-API check:** Affects every run on go1.26.4+. But polish already degrades gracefully to `@latest`.
- **Worth a fix?** Marginal — graceful fallback already exists; a maintainer bumps the pin on the next Go upgrade regardless.
- **Durable fix:** bump the gosec pin (or default to `@latest` with a compatibility note).
- **Related prior retros:** None.

## Prioritized Improvements

### P1 — High priority
| Finding | Title | Component | Frequency | Fallback Reliability | Complexity | Guards |
|---------|-------|-----------|-----------|---------------------|------------|--------|
| F1 (+F2) | Wire oauth2_refresh custom env_vars to the refresh client; register auth set-token | generator | every oauth2_refresh CLI w/ custom env_vars | Low (live-only; often skipped) | small | only assign generic creds when empty |

## Skip
| Finding | Title | Why it didn't make it |
|---------|-------|------------------------|
| F3 | gosec pin won't build on go1.26.4 | Step G: graceful `@latest` fallback already exists; maintainers bump on next Go upgrade. Even-split → default don't-file. |

## Dropped at triage
| Candidate | One-liner | Drop reason |
|-----------|-----------|-------------|
| Windows path/credentials tests | Generated `paths_test.go`/`credentials_test.go` assume POSIX `HOME`, fail on Windows `go test` | printed-CLI / narrow: test-only, no ship impact, pipeline never runs `go test`; Windows-only |

## Work Units

### WU-1: Auth template completeness for oauth2_refresh (from F1, F2)
- **Priority:** P1
- **Component:** generator
- **Goal:** A generated `oauth2_refresh` CLI authenticates with credentials supplied via the spec's custom `env_vars`/config keys, with no hand-edit.
- **Target:** Generator auth/config templates in `internal/generator/` (config `Load`, auth command registration).
- **Acceptance criteria:**
- positive: generate an oauth2_refresh CLI with custom `env_vars`; set them; `doctor` reports auth configured AND a mock-live request carries the `Authorization` header. `auth set-token --help` resolves.
- negative: a CLI whose `env_vars` are already the generic names still authenticates (no double-assign breakage); non-token auth types emit no dangling `set-token`.
- **Scope boundary:** Does not change the auth header format, scopes, or token URL handling; only bridges credential fields and registers the set-token command.
- **Dependencies:** none
- **Complexity:** small

## Anti-patterns
- None observed in the run itself; the auth-template gap is the machine's, not the agent's.

## What the Printing Press Got Right
- Hand-authored internal YAML spec generated cleanly (52 endpoints, 16 resources) on the first valid pass.
- `Config.Headers` map applied to every request gave a clean, single-chokepoint home for the mandatory `orgId` header.
- `ZOHO_DESK_BASE_URL`/`ZOHO_DESK_TOKEN_URL` env overrides handled multi-DC with zero extra work.
- dogfood reliably caught the orphaned `auth set-token` and the novel-feature data-source gate; scorecard hit 95 with accurate dimension scoring.
- Novel-command scaffolds (with flags + smoke tests pre-wired) made the 8 transcendence builds fast and consistent.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Phase 4.85 Output Review — zoho-desk-pp-cli
PASS. All sampled novel commands (triage, sla-radar, agent-load, morning, since) emit parseable JSON with empty-but-valid results on an empty store; no panics/nulls-where-[]-expected. (Wave B: warnings-only; none blocking.)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Phase 4.95 Local Code Review — zoho-desk-pp-cli

Reviewer: subagent over in-scope hand-written files only.

## Autofixed in-place (2)
- HIGH doctor.go: org_id block dereferenced cfg without nil guard → panic when config.Load returns (nil,err). FIXED: wrapped in `if cfg != nil`. Verified: doctor no longer panics on bad config.
- LOW rebalance.go --apply: bailed on first PATCH error, hiding movesApplied/partial progress. FIXED: accumulate per-move failures, always print view with movesApplied + failures[] + stderr warning.

## Accepted / not fixed (1)
- LOW config.go: env-sourced ZOHO_DESK_ORG_ID can persist into on-disk Headers on a later save(). Accepted: orgId is not a secret, env wins on every load so behavior stays correct, and the fix touches the generator-owned persisted() path. Documented, not patched.

## Passed
SQL injection (parameterized/constant), nil/div-by-zero guards, time-fallback correctness, []-not-null marshaling, rebalance write-gating + dogfood cap, resource leaks (Close deferred), orgId env-wins + nil-init.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"schema_version": 1,
"api_name": "zoho-desk",
"run_id": "20260629-104323",
"status": "pass",
"level": "quick",
"matrix_size": 13,
"tests_passed": 13,
"tests_skipped": 11,
"auth_context": {
"type": "oauth2_refresh"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Zoho Desk CLI — Phase 5 Live Acceptance Report

Level: Quick (read-only OAuth scopes; write lifecycle not exercised — no sandbox/approval).
Tests: 13/13 passed, 11 skipped (write commands needing fixtures, correctly classified blocked-fixture).
Gate: PASS (phase5-acceptance.json status=pass).
Auth: oauth2_refresh against the live test portal.

## Live verification (read-only, real portal)
- doctor: all green (Config/Auth/Env Vars/Org ID/API reachable).
- OAuth2 refresh-token flow: WORKS end-to-end (auto-mints access token).
- orgId auto-detected from organizations list and injected on every request.
- agents list: returned the org's agents (the support team).
- departments list: returned the org's 2 departments.
- tickets list: live results.
- sync: pulled ~100 live tickets into local SQLite.
- agent-load --weighted: aggregated 7 assignees over 100 scanned tickets (median weighted load computed).
- triage / morning: produced correct ranked/composed output over real synced data.

## Bug found and fixed during live testing (Printing Press generator issue)
oauth2_refresh with custom env_vars: the ZOHO_DESK_* env vars and desk_* config
keys populated separate ZohoDesk* config fields that were NEVER wired into the
OAuth refresh client (which reads the generic ClientID/ClientSecret/RefreshToken).
Result: env-var auth and desk_* config keys silently failed with 401 despite
valid credentials. FIXED in config.go Load by bridging ZohoDesk* -> generic
fields when the generic ones are empty. Verified: doctor went from
"Auth: not configured" to "configured", live calls succeed. -> RETRO candidate.

## Verdict: ship

## Note: pre-existing Windows unit-test env failures (not regressions)
internal/config paths_test.go and credentials_test.go fail in this Windows
session (HOME vs USERPROFILE home-dir resolution; corrupt-config legacy
fallback). Confirmed failing on the unedited generated code too. Not part of
the shipcheck gate (which passes 7/7); generator never ran go test in its
gates. Flag for retro: generated path/credential tests assume POSIX HOME.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Zoho Desk CLI — Phase 3 Build Log

Manifest transcendence rows: 8 planned, 8 built. Phase 3 will not pass until all 8 ship.
(Novel `stale` dropped pre-build: collided with framework `stale`, which covers the same need.)

## Foundation (generator-provided, Priority 0/1)
- OAuth2 refresh-token client with auto-refresh; orgId injected into every request via Config.Headers (hand-wired in config.go Load).
- Multi-DC via ZOHO_DESK_BASE_URL / ZOHO_DESK_TOKEN_URL env overrides.
- doctor surfaces missing orgId (the #1 Zoho Desk 401 cause).
- 52 generated endpoint commands across 16 resources; SQLite store; sync/search/sql/export/import.

## Transcendence (hand-code, this phase)
1. sla-radar — built: yes
2. agent-load — built: yes
3. triage — built: yes
4. since — built: yes
5. contact-360 — built:
6. morning — built: yes
7. rebalance — built: yes
8. breach-history — built: yes
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Zoho Desk CLI — Polish Result
Verify 100% (34/34), Scorecard 95/100, Dogfood FAIL->PASS, go vet 0, gosec(hand) 0, PII 7->0, tools-audit 0.
Fixes: wired orphaned `auth set-token` into auth tree (generator miss); added `pp:data-source local` to 8 novel commands; replaced example email with PII_EMAIL_EXAMPLE.
Retro candidates: auth set-token not auto-registered; gosec pin v2.21.4 won't compile under go1.26.4; 34 generated-file gosec findings; oauth2_refresh env-var wiring (found in Phase 5).
ship_recommendation: ship; further_polish_recommended: no.
Loading
Loading