A CRM is not one thing — it's a system of record plus half a dozen workflows that all read the same customers. The usual result is half a dozen products (or schemas, or clouds) with sync jobs between them, and a "single customer view" that is really six copies drifting apart. This suite is the opposite: the whole CRM on one DataK3 bucket. Accounts, contacts, leads, opportunities and activities are the shared spine; lead scoring, pipeline forecast, quote/CPQ, campaign attribution and account-360 are workflows that JOIN that spine directly — one copy of the rows, queried by content (SQL), by relationship (graph), and by meaning (vector).

The problem — and the money

The buyer here is the RevOps or sales-systems owner who has to stitch a CRM out of point tools — a scoring app, a forecasting cube, a CPQ, an attribution product — and then keep them in sync. Each workflow is worth a tutorial on its own, and has one. The point of the suite is that they compose with zero glue: a scoring query reads the same leads the core owns; campaign attribution reads the same opportunities the forecast weights; account-360 rolls up a corporate family over the same graph the core seeded. No connector, no nightly export, no reconciliation. The payoff is a hard number the scattered stack cannot produce without a warehouse job — $36,000 of open pipeline across the Acme corporate family, rolled up over a graph three skills contributed to and one assembled — answered as a single query over one copy of the rows.

Seven base skills, each independently live-validated, compose into the suite:

#SkillWhat it ownsReads (shared master)
1crm/coreaccounts, contacts, leads, opportunities, activities, 3× vectors, crm_node/crm_edge— (the root)
2crm/quote-cpqproducts, price_books, quotes, quote_lines, …accounts, opportunities
3crm/lead-to-opportunityorganizations, flows, conversions, …writes into core leads/opportunities
4crm/qualification-scoringlead_scores, scoring_policyleads, opportunity_vectors
5crm/pipeline-forecastpipeline_stages, forecast_snapshots, deal_riskopportunities
6crm/campaign-to-leadcampaigns, attribution, touchpoints, …opportunities, leads
7crm/account-360relationships, account_summary, whitespaceaccounts, products, the graph

This page does not re-teach them — follow each link for the mechanics. Here we show how they assemble into one bucket. The machine-readable manifest is the suites/crm contract.

How it composes

Three rules make seven skills one CRM.

1. One bucket, 36 disjoint tables. Every skill's tables are globally unique names, so they coexist in a single bucket with no collision — 34 SQL/vector tables plus crm_node and crm_edge. Installing the suite is installing the seven skills, in order, into the same --bucket.

2. Masters are owned once, joined everywhere. crm/core owns accounts/contacts/leads/opportunities/ activities. No other skill re-declares them — they JOIN them. A converted org becomes a row in core's leads; the scorer writes lead_scores keyed to that same lead_id; campaign attribution keys to that same opportunity_id. One row, many readers:

You

In the crm bucket, show the two cross-skill joins that prove shared masters: qualification-scoring's lead_scores joined to core's leads, and campaign-to-lead's attribution joined to core's opportunities with a simple ROI.

DODIL MCP tools called
data_sql
Agent

lead_scores ⋈ leads: Jane Doe (acme.io) qualified at 82, Sam Ray (greyparrot.ai) disqualified at 34 — the scorer wrote against the same lead_id core owns. attribution ⋈ opportunities: the Q3 Data Webinar is credited $24,000 on opp-labs (linear model), a 4× ROI on $6k spend — attribution keyed straight to the opportunity core owns. No copy, no join table between products.

3. One graph, assembled once. This is the subtle one. crm_graph is created from a node table and an edge table by a CREATE GRAPH that snapshots its edges at creation — edges added afterwards are invisible. Three skills contribute edges (core's subsidiary_of/works_at, campaign-to-lead's influenced, account-360's partner_of/supplies/…), so no skill can create the graph early. In suite mode every contributor only INSERTs into crm_node/crm_edge; the single CREATE GRAPH is deferred to account-360, the last contributor. Then the family rollup traverses it:

You

In crm, run the single deferred graph assembly last (after all skills have added their edges), then roll up open pipeline across the Acme corporate family — but only over subsidiary_of edges, since partner/supplier edges now share the same graph.

DODIL MCP tools called
data_pg
Agent

With every contributor's edges in place — core's subsidiary_of/works_at plus account-360's partner_of — one CREATE GRAPH crm_graph snapshots them. The family rollup constrained to rel='subsidiary_of' returns $36,000 (Acme Labs $24k + Acme EU $12k). NOTE the composition subtlety: the unconstrained graph_khop('crm_graph',1,2,'in') that worked in the standalone core skill now over-counts to $66,000 — it walks ALL incoming edges, so Greyparrot's partner_of edge leaks into the family. In the assembled graph, type your traversal.

IMPORTANT

The composition subtlety, found live. A traversal that is correct for one skill can be wrong once the suite shares its graph. Standalone, crm/core owns only subsidiary_of/works_at edges, so graph_khop('crm_graph', 1, 2, 'in') returns exactly the family. In the suite, account-360 adds partner_of edges into the same crm_edge table, and the unconstrained 'in' walk over-counts ($66k, not $36k). The fix is to type the traversal (rel = 'subsidiary_of'). Assembling into one graph is the whole point — but it means every graph read must say which edges it means.

Scaffold it — the one-shot

With the DODIL MCP connected, one prompt scaffolds the full suite in order into one bucket:

Scaffold the full CRM suite in one bucket (suites/crm).
 
Bucket: crm. Seed the shared Acme demo dataset. Install the seven base skills IN ORDER into that
one bucket:
  1. crm/core            — 5 masters + 3 VECTOR(2048) tables + crm_node/crm_edge (populate node/edge,
                           but DEFER CREATE GRAPH — suite mode).
  2. crm/quote-cpq       — products first, so account-360 whitespace has a catalog.
  3. crm/lead-to-opportunity — discovery warehouse; conversions write into core's leads/opportunities.
  4. crm/qualification-scoring — lead_scores over core.leads (BANT).
  5. crm/pipeline-forecast — stages/forecast over core.opportunities.
  6. crm/campaign-to-lead — attribution over core.opportunities (linear).
  7. crm/account-360     — relationships/whitespace, add the last graph edges, THEN run the single
                           CREATE GRAPH crm_graph.
 
Then prove the assembly: 36 tables coexist; lead_scores⋈leads and attribution⋈opportunities join;
the family rollup over subsidiary_of = $36,000. Ask me the union questions once (bucket, seed, industry
overlay, product pitch) — don't re-ask what composition already answers.

Want an industry cut? Add overlay: saas | manufacturing | finserv | real-estate — an additive diff on top of the same 36 tables (subscriptions, dealers, households, or listings + gate defaults), never a fork. See the SaaS overlay for the pattern.

Building just part of it

You don't have to install all seven. To build a subset, install crm/core first — it owns the five masters (accounts/contacts/leads/opportunities/activities) every workflow reads — then add only the workflow skills you want. Each workflow tutorial names its master dependencies (its skill contract's consumes block is the machine-readable list): qualification-scoring reads leads/contacts/activities/opportunities; pipeline-forecast reads opportunities/accounts. Each workflow can also run fully standalone on an empty bucket — it ships a stub-masters step that creates just the masters it reads — but once two workflows share the same masters, install crm/core once instead of letting each stub its own. And skip the graph (no CREATE GRAPH) unless you install account-360, which owns the single deferred graph assembly.

The questions, asked once

Composition removes the redundant asks. The suite interview is short:

  • Asked once (shared): the bucket, seed_data, the industry overlay (drives all four overlay diffs at once), and product_pitch (shared by lead-to-opportunity and qualification-scoring — one answer, both gates).
  • Removed by composition: lead-to-opportunity never asks "hand-feed leads or discover?" — its leads and opportunities come from core; account-360 never asks "seed a product catalog?" — products come from quote-cpq; qualification-scoring never re-creates opportunity_vectors — they come from core; campaign-to-lead never asks "project campaign nodes into the graph?" — crm_node/crm_edge are core's, and the single deferred CREATE GRAPH covers everyone.
  • Still per-workflow (genuine knobs): the qualify framework (BANT/MEDDIC), the attribution_model, the forecast stages/probabilities, the CPQ approval_threshold_pct. Real best-practice decisions — the overlay defaults them, you tune them.

Verify

The full single-bucket assembly was live-validated on 2026-09-02 (org IHDIASH, one throwaway bucket, torn down after) — the composition itself, not a re-test of each skill:

# 1) 36 tables from all seven skills coexist in ONE bucket, NO name collision
dodil data table list -b crm | wc -l          # 36 (34 SQL/vector + crm_node + crm_edge), all disjoint
 
# 2) shared master, two skills: qualification-scoring's lead_scores ⋈ core's leads
dodil data sql -b crm "SELECT verdict, count(*) FROM lead_scores GROUP BY verdict"
#   qualified 1 (Jane, 82) · disqualified 1 (Sam, 34)
 
# 3) shared master, two skills: campaign-to-lead's attribution ⋈ core's opportunities
dodil data sql -b crm "SELECT round(sum(weight*amount)) FROM attribution WHERE opportunity_id='opp-labs'"
#   24000  (Q3 Webinar → opp-labs, 4× ROI on $6k)
 
# 4) ONE graph, assembled last; family rollup constrained to subsidiary_of
dodil data pg -b crm "SELECT coalesce(sum(o.amount),0) FROM crm_edge e
  JOIN crm_node n ON n.id=e.src AND n.kind='account'
  LEFT JOIN opportunities o ON o.account_domain=n.biz_key AND o.status='open'
  WHERE e.dst=1 AND e.rel='subsidiary_of'"
#   36000  (naive graph_khop('in') over-counts to 66000 once partner_of shares the graph — type your traversal)

The 36-table coexistence, both cross-skill JOINs, the single CREATE GRAPH, and the $36,000 family rollup are all proven live. The Ignite engines each skill deploys are validated in their own tutorials; the suite test is the data-plane composition — that seven skills share one bucket, one set of masters, and one graph without collision.

Connect your tools

Everything the seven skills wrote lives in the one DataK3 bucket, reachable by your own stack — psql and pgvector drivers over the Postgres wire, a Neo4j driver over Bolt, gRPC for the table engine. data connect crm prints the endpoints; point BI, a dashboard, or your app straight at the live rows. Full, live-validated walkthrough: Connect your tools.

Composes

This page is a composition, not a fork:

  • Seven base skillscrm/core, crm/quote-cpq, crm/lead-to-opportunity, crm/qualification-scoring, crm/pipeline-forecast, crm/campaign-to-lead, crm/account-360 — each its own tutorial and its own ## Test.
  • The suite manifest (suites/crm) — the ordered install DAG, the shared-master wiring, the single deferred graph rule, and the union Q&A above.
  • Overlays (crm/overlays/*) — additive industry diffs that apply on top of the assembled suite.

Read the seven to learn each workflow; read this to assemble them into one CRM on one bucket.