# Health Check

> Auto-verifies onboarding configuration status by querying Supabase. Run during sync/prep to surface what's configured vs missing — no manual checkboxes needed. Use when the user says "health check {client}" or as part of sync/prep workflows.

- Skill: `ivangit-avoca/health-check` (Agent Skill)
- Install (CLI): `npx skillmds@latest add ivangit-avoca/health-check`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ivangit-avoca/health-check/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ivangit-avoca (https://skillmd.com/u/ivangit-avoca)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/ivangit-avoca/health-check

---


# Client Health Check

## When this applies

- Automatically during `sync {client}` or auto-sync (parallel with Gmail/GCal/Attention)
- Automatically during `prep me for {client}` (uses cached data if fresh)
- Explicitly when the user says **"health check {client}"**

## Prerequisites

- `avoca-client.json` must exist in the client folder with an `avoca_client_id` field
- If no `avoca_client_id`, skip the health check silently

## Workflow

### Step 1 — Get team ID

Read `avoca-client.json` from the client folder. Extract `avoca_client_id`. This is the numeric team ID used in all Supabase queries.

If the file doesn't exist or has no `avoca_client_id`, skip the health check and note: `_Health check skipped — no team ID._`

### Step 2 — Get product context

Read `context.md` to determine which products the client has (from `**Products**:` in Quick facts). This determines which sections of the health check to run:

| Product keyword | Sections to check |
|-----------------|-------------------|
| Responder | Core setup, Pre-onboarding, Testing readiness |
| Outbound | Core setup + Outbound-specific |
| Coach | Core setup only |
| Simple Scheduler | Core setup + Simple Scheduler-specific |
| STL / Speed to Lead | Core setup + Outbound-specific (subset) |

If products are blank or unknown, run Core setup + Pre-onboarding + Testing readiness (Responder is assumed default).

### Step 3 — Run consolidated SQL query

Run ONE query against Supabase. Replace `{TEAM_ID}` with the numeric team ID.

- **MCP server:** `user-Supabase`
- **Tool:** `execute_sql`
- **Project ID:** `wmizcewjcybhvkpwpmim`

```sql
SELECT
  -- Team basics
  t.id AS team_id,
  t.name AS team_name,
  t.crm,
  t.completed_onboarding,
  t.features,
  t.ai_assistant_id,
  t.deactivated_at,

  -- ST credentials
  (SELECT COUNT(*) FROM service_titan st
   WHERE st.team_id = t.id
     AND st.tenant_id IS NOT NULL
     AND st.client_id IS NOT NULL) AS st_creds_count,

  -- Responder webhook config
  (SELECT json_agg(json_build_object(
    'live', rwc.live,
    'assistant_id', rwc.assistant_id,
    'uses_booking_windows', rwc.uses_booking_windows
  )) FROM responder_webhook_configs rwc
   WHERE rwc.team_id = t.id) AS responder_configs,

  -- Employee contacts (active associates)
  (SELECT COUNT(*) FROM associates a
   WHERE a.team_id = t.id AND a.active = true) AS active_associates,

  -- Transfer destinations
  (SELECT COUNT(*) FROM transfer_destinations td
   WHERE td.team_id = t.id AND td.active = true) AS active_transfers,

  -- On-call departments
  (SELECT COUNT(*) FROM on_call_departments ocd
   WHERE ocd.team_id = t.id AND ocd.active = true) AS on_call_depts,

  -- On-call ST sync
  (SELECT json_build_object(
    'enabled', ocs.enabled,
    'last_synced_at', ocs.last_synced_at,
    'last_sync_error', ocs.last_sync_error
  ) FROM on_call_service_titan_sync_configs ocs
   WHERE ocs.team_id = t.id) AS on_call_sync,

  -- Booking windows (arrival windows)
  (SELECT COUNT(*) FROM arrival_windows aw
   WHERE aw.team_id = t.id) AS booking_windows_count,

  -- Service areas
  (SELECT COUNT(*) FROM service_areas sa
   WHERE sa.team_id = t.id) AS service_areas_count,

  -- Dispatch fees
  (SELECT COUNT(*) FROM dispatch_fees df
   WHERE df.team_id = t.id) AS dispatch_fees_count,

  -- Phone numbers
  (SELECT json_agg(json_build_object(
    'phone_number', pn.phone_number,
    'has_vapi', pn.vapi_phone_number_id IS NOT NULL
  )) FROM phone_numbers pn
   WHERE pn.team_id = t.id) AS phone_numbers,

  -- VAPI assistant configs
  (SELECT COUNT(*) FROM assistant_configs ac
   WHERE ac.team_id = t.id
     AND ac.vapi_assistant_id IS NOT NULL) AS vapi_assistant_count,

  -- Feature flags for this team
  (SELECT json_agg(json_build_object(
    'key', fft.flag_key,
    'enabled', fft.enabled
  )) FROM feature_flag_targets fft
   WHERE fft.target_type = 'team'
     AND fft.target_id = t.id::integer) AS feature_flags,

  -- Knowledge base
  (SELECT COUNT(*) FROM knowledge_bases kb
   WHERE kb.team_id = t.id) AS knowledge_base_count,

  -- CRM sync config (crm_service_titan uses integer team_id)
  (SELECT json_agg(json_build_object(
    'entity', sc.entity,
    'enabled', sc.is_enabled
  )) FROM crm_service_titan.sync_config sc
   WHERE sc.team_id = t.id::integer) AS crm_sync_config,

  -- Outbound text agents
  (SELECT COUNT(*) FROM text_conversation_configs tcc
   WHERE tcc.team_id = t.id AND tcc.enabled = true) AS outbound_agents_count,

  -- Workflows
  (SELECT COUNT(*) FROM workflows w
   WHERE w.team_id = t.id) AS workflows_count,

  -- Campaigns
  (SELECT COUNT(*) FROM workflow_campaigns wc
   WHERE wc.team_id = t.id) AS campaigns_count,

  -- Notifications (uses integer team_id)
  (SELECT COUNT(*) FROM notification_configurations nc
   WHERE nc.team_id = t.id::integer) AS notification_configs_count,

  -- Simple scheduler
  (SELECT COUNT(*) FROM simple_scheduler_config ssc
   WHERE ssc.team_id = t.id::text) AS simple_scheduler_count,

  -- Onboarding completion tracking
  (SELECT json_build_object(
    'booking_windows_completed', tob.booking_windows_completed,
    'service_areas_completed', tob.service_areas_completed
  ) FROM team_onboarding tob
   WHERE tob.team_id = t.id) AS onboarding_status

FROM teams t
WHERE t.id = {TEAM_ID};
```

### Step 4 — Interpret results

Use the query results to build a health check report. Apply these rules:

**Core setup (all products):**

| Check | Pass condition | Fail message |
|-------|---------------|--------------|
| Team exists | Row returned, `deactivated_at` is null | Team not found / deactivated |
| CRM configured | `crm` is not null | CRM not set |
| ST API credentials | `st_creds_count` >= 1 | ST credentials not configured |
| Responder webhook | `responder_configs` is not null and has at least one entry | No responder config |
| Responder live | Any entry in `responder_configs` has `live` = true | Responder not live |
| VAPI assistant | `vapi_assistant_count` >= 1 OR `ai_assistant_id` is not null | No VAPI assistant linked |
| Products enabled | `features` array is non-empty | No products enabled — check `features` |

**Pre-onboarding (Responder, Outbound):**

| Check | Pass condition | Fail message |
|-------|---------------|--------------|
| Service areas | `service_areas_count` >= 1 | No service areas configured |
| Booking windows | `booking_windows_count` >= 1 | No booking windows configured |
| Fees | `dispatch_fees_count` >= 1 | No dispatch fees configured |
| Knowledge base | `knowledge_base_count` >= 1 | Knowledge base not populated |
| Phone numbers | `phone_numbers` is not null | No phone numbers provisioned |

**Testing readiness (Responder):**

| Check | Pass condition | Fail message |
|-------|---------------|--------------|
| Employee contacts | `active_associates` >= 1 | No employee contacts configured |
| Transfer destinations | `active_transfers` >= 1 | No transfer destinations configured |
| On-call calendar | `on_call_depts` >= 1 | No on-call calendar configured |
| On-call ST sync | `on_call_sync.enabled` = true and `last_sync_error` is null | On-call sync not enabled / has errors |

**Outbound-specific:**

| Check | Pass condition | Fail message |
|-------|---------------|--------------|
| CRM sync enabled | `crm_sync_config` has entries with `enabled` = true | CRM sync not enabled |
| Outbound agent | `outbound_agents_count` >= 1 | No outbound agent configured |
| Workflows | `workflows_count` >= 1 | No workflows created |
| Campaigns | `campaigns_count` >= 1 | No campaigns created |
| Notifications | `notification_configs_count` >= 1 | No notifications configured |

**Simple Scheduler-specific:**

| Check | Pass condition | Fail message |
|-------|---------------|--------------|
| SS config | `simple_scheduler_count` >= 1 | Simple Scheduler not configured |
| SS feature flag | `feature_flags` contains `simple_scheduler` with `enabled` = true | Feature flag not enabled |

### Step 5 — Write to context.md

Write the results into the `## System health` section of `context.md`. Replace the entire section contents each time.

Format — use `[x]` for passing checks, `[ ]` for failing, with counts/details in parentheses:

```markdown
## System health
<!-- health_checked: {ISO 8601 timestamp} -->

### Core setup
- [x] Team exists (ID: 2297, Name: OHA)
- [x] CRM: ServiceTitan
- [x] ST API credentials configured
- [x] Responder webhook configured (live)
- [x] VAPI assistant linked
- [x] Products: Responder, Outbound

### Pre-onboarding
- [x] Service areas (3 configured)
- [x] Booking windows (12 configured)
- [x] Fees (2 dispatch fees)
- [ ] Knowledge base — not populated
- [x] Phone numbers (2 provisioned, 1 with VAPI)

### Testing readiness
- [x] Employee contacts (4 active)
- [x] Transfer destinations (2 active)
- [ ] On-call calendar — no departments configured
- [ ] On-call ST sync — not enabled

### Outbound
- [x] CRM sync enabled (customers, jobs, invoices)
- [ ] Outbound agent — not configured
- [ ] Workflows — none created
- [ ] Campaigns — none created
- [ ] Notifications — not configured
```

Only include product-specific sections when the client has that product.

### Step 6 — Surface in prep/sync

When this runs as part of `prep me for {client}`:
- Include a **"Missing config"** section in the brief listing only the unchecked items
- Frame them as talking points: "You may want to address: on-call calendar, knowledge base"

When this runs as part of `sync {client}`:
- Write silently to `context.md`
- Only mention in response if there are notable changes (e.g., something that was missing is now configured)

When run standalone (`health check {client}`):
- Show the full health check report to the user
- Highlight any items that need attention

## Error handling

- If the Supabase query fails, skip the health check and note: `_Health check failed — Supabase query error._`
- If a specific sub-query returns unexpected data, mark that check as `[?] {check} — could not verify` and continue
- Never block the sync/prep workflow because of a health check failure

## Caching

The `<!-- health_checked: ... -->` timestamp controls freshness:
- During auto-sync: always re-run (it's one cheap query)
- During prep: use cached data if `health_checked` is within 6 hours; otherwise re-run
- Standalone `health check`: always re-run (fresh data)

