# /crm — Conversint CRM (log, capture, report)
Shared operating boundaries live in `references/skill-operating-standards.md`; shared Google Workspace access rules live in `references/google-workspace-access.md`. This skill owns CRM reads and writes; sibling skills should offer `/crm log` or `/crm capture` handoffs instead of writing CRM rows directly.
One skill, one resource: the Conversint CRM Google Sheet. Three modes — a freeform writer, a file-driven batched writer, and a read-only reader.
Mode selection
Pick the mode from the first positional argument, or infer from input shape:
| First arg | Mode | Then load | |---|---|---| | `log` | log (write) | `references/log.md` | | `capture` | capture (write, file-driven) | `references/capture.md` | | `report` | report (read-only) | `references/report.md` | | *(freeform text describing an event: "called Ethan...", "we lost...", "move X to Qualified")* | log | `references/log.md` | | *(an opportunity follow-up reminder: "remind me to follow up with Acme Tuesday")* | log + Calendar handoff | `references/log.md`, then `/calendar` | | *(a file path, or text like "pull updates from this", "log what happened in this meeting")* | capture | `references/capture.md` | | *(a question: "pipeline?", "what's overdue?", "who haven't we talked to?", bare with no args)* | report | `references/report.md` |
If the input is genuinely ambiguous, ask. Don't silently pick between write and read.
Preconditions (all modes)
Before anything else, confirm auth:
```bash scripts/python - <<'PY' import sys; sys.path.insert(0, 'lib') import crm print(crm.auth_ok()) PY ```
If `crm.auth_ok()` returns `False`, stop and tell the user to re-authenticate from the repo root:
```bash scripts/gws auth login -s sheets,drive,docs,gmail,calendar ```
Do not call `scripts/gws auth login` from inside the skill — auth is a human-driven setup step only.
Data model (all modes)
The CRM is a Google Sheet with four data tabs plus a Dashboard. All reads/writes go through `lib/crm.py`. The spreadsheet ID and tab names live in `config/crm.json`.
**Tabs (via `crm.TABS`):**
- `companies` — `company_id` (e.g. `COMP-007`), `Name`, `Source`, `Created`
- `contacts` — `contact_id` (e.g. `CONT-008`), `Company`, `First`, `Last`, `Email`, `Relationship`, `Created`
- `opportunities` — `opp_id` (e.g. `OPP-004`), `Company`, `Primary Contact`, `Description`, `Stage`, legacy `Est. Value` / `Probability`, structured `Estimated MRR` / `Estimated One-Time Value` / `Expected Close` / `Forecast Confidence`, `Next Action`, `Next Action Due`, `Last Interaction`, `Created`
- `activities` — `Date`, `Type` (`call` / `email` / `meeting` / `intro` / `note` / `stage change` / `other`), `Company`, `Contact` (full name), `Opportunity` (opp ID, or blank), `Summary`, `Outcome`
**Column conventions:**
- `Contact` on Activities: full name (scannable), not the ID
- `Opportunity` on Activities: opp ID like `OPP-004` (stable even if the description changes)
- Activities are for pipeline, delivery, and relationship interactions only. Do **not** write Activity rows for data management tasks such as enrichment runs, field cleanup, formatting fixes, deduping, or back-filling contact details unless the user explicitly asks for an audit trail entry.
- `Primary Contact` on Opportunities: full name
- **All dates: `YYYY-MM-DD`**. Anything else breaks Dashboard formulas.
- **Structured forecast fields:** `Estimated MRR` and `Estimated One-Time Value` are non-negative numeric amounts; `Expected Close` is `YYYY-MM-DD`; `Forecast Confidence` is `Low`, `Medium`, or `High`. Preserve legacy `Est. Value` and `Probability` as context only; do not total their free-form text.
- **`Notes` on any tab is a log field — prepend, never overwrite.** Read the current value, prefix the new entry as `YYYY-MM-DD: <content>`, write the combined string back. See `references/crm-handoff.md` § "Notes field" for the full convention.
**Stage and Relationship vocabulary:** see `config/crm.json` and the Reference tab of the sheet.
**Expected next-step cadence:** when proposing a Next Action / Next Action Due on an opportunity, follow the discovery→proposal motion in `references/sales-playbook.md` (e.g. a proposal commitment lands within 48 hours of a discovery call). Read it when a log or capture write needs to infer a sensible next step or due date.
CRM reminders → Calendar by default
For a reminder about an opportunity follow-up, resolve the active opportunity and update its `Next Action` and `Next Action Due`. Unless Patrick explicitly says **CRM only**, hand the reminder to `/calendar` as well. Calendar owns the event write; do not reimplement its Calendar API or CLI behavior here. If the named contact or account has no active opportunity, ask whether to create one or handle the reminder in Calendar only—do not create a placeholder deal. Post-Won delivery reminders belong in `/project-manager`, which owns the delivery task and its Calendar reminder.
Preserve Calendar's draft-first safeguard: present the Calendar event preview (calendar, summary, date/time, attendees, and location/Meet details) and wait for Patrick's explicit approval before creating it. Do not add external attendees or send invitations. A Calendar handoff needs a date and time; ask for either missing scheduling detail instead of inventing one. A **CRM only** reminder needs a date but does not need a time.
For a CRM-originated Calendar reminder, use a 15-minute transparent event, with a zero-minute popup, no attendees, and no outbound updates unless Patrick asks for different handling. Supply its start and end times to Calendar.
Before handing off a revised or rescheduled reminder, read the opportunity `Notes` for its most recent active Calendar event ID, not a cancellation marker. Pass that ID to `/calendar` so it can preview an event update instead of creating a duplicate. Include the opportunity ID in the Calendar handoff. After Calendar confirms a create or update, re-enter this skill with the named return handoff `/crm log calendar-reminder-created` or `/crm log calendar-reminder-updated`, including the opportunity ID, event ID, and event link. That return handoff prepends the current event reference to `Notes`. A reminder by itself is not a CRM Activity; log an Activity only when there was an underlying pipeline, delivery, or relationship interaction. Personal reminders belong on the personal calendar surface, not the Conversint work calendar; use the personal connector or the `prim` Calendar workflow without CRM changes. Other non-CRM work reminders should use the skill that owns the work.
Treat a cancellation marker as invalidating every earlier active reference with the same event ID. When resolving an event from append-only `Notes`, select only an active reference whose ID has no newer cancellation marker; otherwise create a new Calendar event.
When a CRM update clears `Next Action` or moves an opportunity to `Won`, `Lost`, or `Dormant`, check `Notes` for its active Calendar reminder. Unless Patrick said **CRM only**, route its deletion to `/calendar` for the same required preview and approval. On success, re-enter this skill with `/crm log calendar-reminder-deleted`, including the opportunity and event IDs; that return handoff prepends a cancellation marker to `Notes` so future reminder requests do not reuse it. If Patrick said **CRM only**, leave the event unchanged and say that it remains scheduled.
`lib/crm.py` essentials (write modes)
```python import sys; sys.path.insert(0, 'lib') import crm
crm.find_contact(query) # fuzzy contact lookup crm.find_company(query) # fuzzy company lookup crm.read_tab(crm.TABS["opportunities"]) crm.append_row(crm.TABS["activities"], {...}) crm.update_row_by_id(crm.TABS["opportunities"], "opp_id", "OPP-004", {...}) crm.next_id(crm.TABS["companies"], "company_id", "COMP") crm.today() # YYYY-MM-DD crm.SPREADSHEET_URL # always surface this in write/read reports ```
Shared output rule
**Always print `crm.SPREADSHEET_URL` in the output report** — write or read — so the user gets a one-click link to inspect the sheet. Per `CLAUDE.md`, this applies to every skill or ad-hoc script that touches the CRM.
Typical write-report shape:
``` ✓ Logged 2026-04-22 · call · Ethan Roebuck · OPP-004 ✓ OPP-004 Next Action → "Send revised SOW" due 2026-04-29 Sheet: <crm.SPREADSHEET_URL> ```
Do not auto-save to Drive
Per repo convention (`CLAUDE.md`), skills never auto-invoke `/gdrive`. If the user wants a snapshot report mirrored to Drive, they will run `/gdrive` themselves.
Spreadsheet artifact reports
Routine CRM row reads and writes follow this skill's mode-specific references. If Patrick asks for a separate CRM snapshot, model, or spreadsheet artifact derived from CRM data, read `references/spreadsheet-artifact-standards.md` before creating it so formatting, formulas, and verification match the shared chiefOS spreadsheet rules.
Next
Load the mode-specific reference — `references/log.md`, `references/capture.md`, or `references/report.md` — and follow it.