> For the complete documentation index, see [llms.txt](https://docs.kula.digital/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kula.digital/for-developers/graph.md).

# Graph & relationship tools

Kula Intelligence derives a **relationship graph** from the attendance and sales data your [connectors](/your-data-sources/connectors.md) already feed in. It is a read-only analytics layer: no new data, no external calls — just a scored view of who attends with whom, who depends on which coach, and which members are drifting.

This page covers what the graph models, the at-risk signals it emits, and the tools that read it. For the raw tables, see the [schema reference](/for-developers/schema.md).

## What the graph models

The graph is **tripartite** — three entity types (members, staff, locations) connected by five classes of weighted edge:

| Edge class | Between           | Captures                                  |
| ---------- | ----------------- | ----------------------------------------- |
| **MS**     | member ↔ staff    | who a member trains with — the coach bond |
| **ML**     | member ↔ location | attachment to the studio/space itself     |
| **MM**     | member ↔ member   | the peer community (who attends together) |
| **SL**     | staff ↔ location  | where a coach works                       |
| **SS**     | staff ↔ staff     | the cover / co-teach network              |

Edges are **time-decayed** (recent activity counts for more than old) and **windowed** (stale relationships age out), so the graph reflects *current* relationships, not lifetime totals. Each edge class decays on its own half-life and ages out at its own **drop-off window** — a coach bond (member↔staff) fades faster than facility/organisational bonds. Every edge carries a **tier** (`strong`/`warming`/`cooling`/`cold`/`lapsed`) derived from how recently it was last seen plus its 30-day **trend**, so you can read *where* a relationship is decaying and over *what timeframe* — see `get_staff_relationship_review` for the per-teacher breakdown.

## Resilience and vulnerability quadrants

On top of the edges, the graph computes a per-member, per-location **resilience** score: how likely a member is to stay if their favourite coach left. A member anchored by the location, their peers, and a varied routine is resilient; one held only by a single coach bond is not.

Resilience is **constraint-modulated** by two factors:

* **Activity affinity (A)** — how broadly the member uses the location (many instructors and classes vs. a single specialist slot).
* **Contract commitment (C)** — the switching cost of their plan (a recurring membership vs. pay-as-you-go).

Together, A and C place each member in one of four **vulnerability quadrants**:

| Quadrant               | Affinity | Commitment | How to read it                                                      |
| ---------------------- | -------- | ---------- | ------------------------------------------------------------------- |
| `resilient_enthusiast` | high     | high       | Safe — broad routine, committed plan.                               |
| `constrained_captive`  | low      | high       | Committed but brittle — a single-coach specialist on a sticky plan. |
| `flexible_explorer`    | high     | low        | Low friction to leave, but broadly engaged.                         |
| `specialist_nomad`     | low      | low        | **Most vulnerable** — narrow routine, no commitment.                |

> The exact scoring is tuned per deployment and not published. The tools return the resulting scores and quadrant labels so you can act on them; the [`list_quadrant_members`](#list_quadrant_members) tool lists the actual members in any quadrant.

There is also a per-**location** operational resilience: the share of staff who have a cover partner, and the single points of failure (coaches who have none). Read it with [`get_location_context`](#get_location_context).

## At-risk signals

The graph emits an **at-risk queue** — derived facts that a member may need attention now. Read the open queue with [`get_open_signals`](#get_open_signals).

| Signal                | Fires when                                                                                                           |
| --------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `drift`               | A previously-regular member has gone quiet for a stretch.                                                            |
| `freq_softening`      | Attendance cadence is slowing while they're still showing up — the early warning before drift.                       |
| `missed_second_visit` | They paid, came once, and never returned.                                                                            |
| `pause_drift`         | A paused member isn't resuming.                                                                                      |
| `at_risk_threshold`   | Their constraint-modulated resilience has crossed a low threshold — a structural signal, not just a behavioural gap. |
| `milestone`           | A round-number class count — a positive moment worth celebrating.                                                    |

> **Boundary.** Kula Intelligence *computes and exposes* these signals. It never composes or sends a message — that belongs to the studio's own action layer. Everything on this page is read-only.

## The relationship-context tools

These tools return **pre-scoped relationship context** — richer than a raw table read, and automatically filtered to what your [permission level](/connect-claude-and-access/scopes.md), your relationship to the entity, and your **declared purpose** allow.

Every call takes a required `purpose` argument, which shapes the response:

* **Analysis purposes** (`retention_analysis`, `departure_simulation`, `cohort_analysis`) **anonymise** members — names become stable pseudonyms and the underlying ids are dropped, so you can study patterns without handling identities.
* **Action purposes** (`action_board`) return real names so an operator can act.
* **Service-delivery purposes** (`whisper_generation`, `pre_class_brief`) **withhold the resilience and vulnerability analytics** — those scores are for analysis, not front-line delivery.

Other accepted purposes: `ai_coach_session`, `member_self_access`, `staff_self_access`. A denied request returns an **empty result**, never an error that would confirm the entity exists.

| Tool                            | Min level  | What it does                                                                                                                                                                     |
| ------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `get_member_context`            | operations | One member's bundle: core facts, per-location resilience + quadrant, and affinity routing.                                                                                       |
| `get_staff_context`             | operations | A staff member's profile, concentration (members who rely on them) and the locations they work.                                                                                  |
| `get_staff_relationship_review` | operations | A teacher's members bucketed by relationship tier (strong/warming/cooling/cold/lapsed) with each bond's timeframe and trend — the connection-decay breakdown for a staff review. |
| `get_location_context`          | operations | A location's operational resilience and the *aggregate* spread of members across the four quadrants.                                                                             |
| `list_quadrant_members`         | operations | The members **in** a quadrant at a location — the act-on-it drill-down from the aggregate.                                                                                       |
| `simulate_departure`            | operations | Who would be at risk if a given staff member left a location, ranked most-vulnerable first.                                                                                      |
| `get_entity_edges`              | operations | The raw edges around a member/staff/location, with class, weight and recency.                                                                                                    |

### `get_member_context`

One member's full relationship picture. **Parameters:** `member_id` (required), `purpose` (required). Returns the member's core facts, per-location resilience (score, activity affinity, contract commitment, vulnerability quadrant), the affinity routing rollup (primary coach, top-3 staff, concentration), and any operator/AI annotations. Resilience is withheld under service-delivery purposes.

### `get_staff_context`

**Parameters:** `staff_id` (required), `purpose` (required). Returns the staff member's profile, their concentration rollup (how many members rely on them — both routing value and defection risk), and the locations they work. Heavily restricted: a relationship-bound staff credential may only query itself.

### `get_staff_relationship_review`

The connection-decay breakdown for a teacher (staff) review. **Parameters:** `staff_id` (required), `purpose` (required), `tier` (optional — restrict the member list to one of strong/warming/cooling/cold/lapsed), `limit` (optional, default 200, max 500). Returns the teacher's members grouped by relationship **tier**, and for each member: the member↔staff edge weight, `last_observed_at`, `days_since`, `trend` (rising/flat/falling), and 30-day/90-day/total attendance with this teacher. A `summary` gives the full tier distribution even when the member list is `limit`-capped. The tier is derived from how recently the member last trained with the teacher (relative to the member↔staff half-life and drop-off window) plus the 30-day trend — so "where the relationship is decaying, and over what timeframe" reads straight off the buckets. Same restriction as `get_staff_context`: a relationship-bound staff credential may only review itself. Reads precomputed `graph.edge` + `graph.connection_score` — no write.

### `get_location_context`

**Parameters:** `location_id` (required), `purpose` (required). Returns the location's core facts, operational resilience (cover depth, single points of failure), and the **aggregate** distribution of members across the four vulnerability quadrants. No member-level PII — counts only.

### `list_quadrant_members`

The act-on-it complement to `get_location_context`: where the latter gives counts, this lists the individual members. **Parameters:** `location_id` (required), `purpose` (required), `quadrant` (optional — one of the four quadrant names; omit for all classified members), `max_resilience` (optional, 0–1 — only members at or below this resilience ceiling, to target the at-risk tail), `limit` (optional, default 100, max 500). Each member comes with their resilience, activity affinity, contract commitment, quadrant, primary-staff routing and a `risk_category`, ranked most-vulnerable first. Names follow your purpose (anonymised under analysis, real under `action_board`).

### `simulate_departure`

**Parameters:** `staff_id` (required), `location_id` (required), `purpose` (required), `limit` (optional, default 100, max 500). Models the impact if a staff member left: returns the members connected to them, each with edge weight, constrained resilience and a risk category, ranked most-vulnerable first. Reads precomputed weights only — no write.

### `get_entity_edges`

**Parameters:** `entity_id` (required), `entity_type` (required — `member` | `staff` | `location`), `purpose` (required), `min_weight` (optional, default 0), `limit` (optional, default 100, max 500). Returns the active edges connected to the entity, with edge class (MS/ML/SL/MM/SS), kind, weight, tier and recency.

## The summary tools

Lightweight rollup reads that don't take a purpose — handy for quick routing questions.

| Tool                      | Min level  | What it does                                                                                     |
| ------------------------- | ---------- | ------------------------------------------------------------------------------------------------ |
| `get_member_affinity`     | operations | A member's primary coach, top-3 staff, and how concentrated their affinity is on that one coach. |
| `get_staff_concentration` | operations | Per-staff routing value and defection risk (members whose primary affinity is this coach).       |
| `get_open_signals`        | operations | The open at-risk queue. Optional `signal_type` filter; highest severity first.                   |
| `get_cac_by_cohort`       | analytics  | Customer-acquisition cost by cohort (a marketing rollup, listed here for convenience).           |

## Querying the raw tables

For anything the tools don't shape for you, query the `graph.*` schema directly with `execute_query` (SELECT/`WITH` only):

```sql
-- the open at-risk queue, most severe first
SELECT person_id, signal_type, severity, context, detected_at
FROM graph.risk_signal
WHERE resolved_at IS NULL
ORDER BY severity DESC, detected_at DESC;

-- the most vulnerable members at a location
SELECT person_id, vuln_quadrant, resilience_constrained
FROM graph.member_resilience
WHERE location_id = '…'
ORDER BY resilience_constrained ASC;
```

See the [schema reference](/for-developers/schema.md) for the `graph.*` tables.

## Freshness

The graph is **derived**, so it lags its inputs until it rebuilds. It refreshes automatically after an import or a connector restate that changes its inputs (attendance, members, plans), and a nightly sweep is the backstop. Scores and signals therefore reflect the most recent build, not the live second.

## Where to go next

* [Tool reference](/for-developers/tools.md) — the complete tool catalogue.
* [Schema reference](/for-developers/schema.md) — the `graph.*` and other tables.
* [Permission levels](/connect-claude-and-access/scopes.md) — what each level can see.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.kula.digital/for-developers/graph.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
