A customer success manager finishes a call with a strategic account. The recorder does its job: within ninety seconds the transcript is written to the CRM, logged against the account, summarised into a note, and two follow-up tasks are created. By every measure of integration health, this worked. The systems talked. Nothing was dropped.
Eleven minutes into that call, the customer said this:
“We're not signing the renewal until case #48213 is closed. It's been open six weeks, and my CFO has already asked me twice why we're paying for something that doesn't work.”
That is the most important sentence anyone said all quarter. It ties a support case to a renewal opportunity, a contract date and the mood of the budget holder who has to sign it - and it answers the question your CRO will ask on the forecast call in three weeks.
Now go and find it in a system.
The case exists in Zendesk, and someone is working it. The opportunity exists in Salesforce with a close date. The call exists, attached to the account. Three real records, correctly stored, correctly integrated - and the relationship between them exists nowhere at all. The recorder didn't create it: “call blocks renewal via case” is not an edge its data model knows about. The Zendesk–Salesforce integration didn't create it: it syncs cases to accounts, not cases to opportunities-at-risk. No human created it, because that would mean opening three tabs to update a field nobody reads. So the relationship stays where it was born: in a sentence, in a transcript, in a system that treats that transcript as an attachment.
This is a dark join - a relationship that is real, load-bearing and actionable in your business, and exists in no schema anywhere in your systems. The relationship isn't in the data. It's in the work. Once you start looking, you find your business runs on them almost entirely.
The name is deliberate. Dark data is the established term for everything an enterprise collects and never uses - stored, indexed, paid for, never read. A dark join is the harder problem next to it: dark data is information you have and ignore; a dark join is a relationship you never had. Dark data can at least be mined. There is nothing here to mine - the relationship was never written down as data at all.
Your join keys are in the conversation
To make this concrete, take the question your executive team actually asks - which renewals are blocked by open support issues? - and try to write it.
SELECT o.name, o.close_date, c.case_number, c.status
FROM opportunities o
JOIN calls ON o.account_id = calls.account_id -- fine
JOIN cases c ON ??? -- there is no key
WHERE o.close_date < CURRENT_DATE + 90
There is no key. There has never been a key - not because someone forgot to model it, but because the fact that this case blocks this renewal was never data. It was work. It happened in the eleventh minute of a Tuesday call and was restated four more times that week: a follow-up email, a Slack thread, a comment on a shared doc, a forward to the platform team. Five declarations of the same relationship, each entering your systems as an unindexed string in a text blob, hanging off a record that couldn't represent it.
You cannot join on it, ETL it, or MDM your way to it. Eighteen months of a systems integrator won't produce the key either, because integration projects move data that exists - and this relationship never existed as data. Everything else follows from that:
A relationship that was never recorded cannot be derived from the records. The relationship isn't in the data. It's in the work.
Why we have been looking in the wrong place
The assumption underneath enterprise data practice - EAI, MDM, the data lake, the modern warehouse, now the enterprise knowledge graph - is that relationships live in the schema. Resolve the entities, reconcile the identifiers, build the golden record. When a relationship is missing, the diagnosis is always the same: the data isn't clean enough yet, so fund another phase.
For one class of relationship this is correct. Is ACME Corp the same customer as Acme Corporation Ltd? is a schema question with a schema answer, and MDM is good at it. But look at what your organisation is actually trying to find out. Which renewals are blocked by unresolved engineering work? Which accounts mentioned the competitor we just lost two deals to? Which roadmap commitments were made verbally in a QBR and never entered anywhere? Why did this account go quiet? Not one is an entity-resolution problem. Every one is a relationship a human declared plainly, in the course of doing the job, and never typed into a system. MDM is an extremely expensive answer to a question nobody was asking.
The deeper problem is direction. Every tool built for this works the same way round: take the human artifact and attach it to the record. The recorder attaches the call to the account; the email plugin attaches the thread to the contact; the support integration attaches the ticket to the account. Each is a one-way association from unstructured content into the one structured record the tool happened to know about. But the call is not about the account. It is about a renewal, a support case, a competitor, a missed commitment, a person who is leaving and a contract clause - and the account is simply the only one of those the recorder could represent. Every other relationship in that call was discarded at ingest, not because it was unimportant but because there was nowhere to put it. The same goes for the email, the thread and the doc: each carried a dozen relationships in, and each had exactly one place to put them.
So here is the inversion:
It is not that the work belongs to the record. It is that the work tells you which records belong together.
Your database is not a map of your business. It is a log of it - an artifact of work already done, recording what someone remembered to enter. The structure of the work lives in the action layer: the tools your people use to do the work, as opposed to the systems they are required to log the work into. That layer isn't the messy exhaust of the real system; it is where the relationships are declared. The database is the exhaust.
The action layer is bigger than the meeting
It would be easy to read the opening story as a transcription problem. It isn't; calls are just the most vivid case. The action layer is the entire human surface of your business: meetings and their transcripts, email, Slack and Teams, shared documents and the comment threads on them, and the bodies of tickets as distinct from their fields.
The reason this layer is so rich is that your people treat these as productivity tools, not databases. Nobody thinks of a Slack message as data entry - no required field, no validation rule, no admin asking why the picklist is blank. So when someone needs to convey that this case is blocking that renewal, they simply say it: accurately, with identifiers, without resistance. In the CRM, the same person faces a form with no field for “blocks,” none for “same root cause as,” none for “champion went quiet because of this” - so the relationship is mangled into a free-text note or, far more often, not entered at all. The schema is a filter, and it filters out precisely the relationships nobody anticipated years ago.
That is the quiet asymmetry: the systems designed to capture structure are the ones least able to capture the structure that matters, and the tools nobody considers systems of record are where the real structure gets declared. Your people are not failing to document their work. They document it constantly and precisely, because that is how coordination works - “Blocked by NOD-231.” “Duplicate of the ticket Sarah raised last week.” “Same root cause as the Contoso deployment.” “Per section 4 of the MSA.” A meeting agenda doc listing seven ticket numbers, two roadmap items and the contract is a dense little graph, hand-built by someone who understood the account, sitting in a folder, relating to nothing.
Why this is suddenly urgent
Dark joins have existed for as long as enterprises have had both databases and meetings. What changed is that you are now being asked to deploy agents on top of them.
Every AI initiative currently stalled in your organisation is stalled for the same reason, and it is almost never the model. Ask an agent “what's the risk on the Contoso renewal?” and it does exactly what it was built to do: retrieve the account record, retrieve some documents mentioning Contoso, and produce a fluent summary of the fields you already had. It will not tell you the renewal is blocked by a six-week-old case, because nothing in its context says those two things are related. RAG retrieves documents; it does not know which records a document is about. No amount of prompt engineering, context window or model upgrade supplies that, because the information was discarded at ingest - long before any model was involved. And it compounds: every new integration you add contributes more nodes and zero new edges. You can integrate forever and the graph stays sparse.
The good news is the corollary. This is a data-layer problem sitting underneath an agent problem: fix it once, and every agent you deploy afterwards inherits the fix.
Measure your own dark join rate
Don't take any of this on assertion - there is a number you can put on it, for one account, in an afternoon. Take a sample of the artifacts attached to one important account - calls, email threads, chat, shared docs - and count the references pointing at something outside the record each is filed against: a ticket number, a Jira key, a contract clause, another document, a competitor, a roadmap commitment. Then count how many of those exist as a traversable link in a system you own.
Unlinked references ÷ total references is your dark join rate.
The first count typically comes back well north of half; the second, close to zero. Email and document comments score worst, because no vendor has ever built even the naive one-way integration for them. The ratio is the honest measure of how much of your operating reality your systems can represent - a better number to take into a budget conversation than another data-quality score, because it measures what your executives mean when they say the CRM is useless. And a high rate is normal: every environment we have seen scores high, including ours. It is structural, not a hygiene failure.
We've published the instrument that does the counting - free, unbranded, CC BY 4.0 - as the Dark Join Diagnostic. It keeps the tally as you read, works out the rate and the split by surface, and builds a ledger of the specific relationships in your business that exist in no system. That ledger, more than the percentage, is what ends the argument. Nothing leaves your browser.
How you find them: ingestion chaining
If you accept the premise, the design follows: something that observes the action layer and precipitates a graph from it. Three mechanisms do most of the work - at Noded we call the combination ingestion chaining, and it is the answer to “fine, but how do you get the relationship out of the work?”
Interaction rank. Treat every touch as an edge - a forward, a mention, a status change, a reply, even a read. Records the work keeps reaching for climb in rank. This is PageRank applied to enterprise work rather than the web, and it inherits PageRank's central virtue: it never has to be told what matters, because importance is a property of the link structure.
Temporal decay. Importance decays unless renewed; a record nobody has touched for a business cycle falls out of the active chain. This is what stops the graph becoming another lake - the axis most enterprise knowledge systems fail on. They accumulate, they never forget, and within a year the signal is buried under everything that used to be true.
Intent filter. The one that decides whether the whole thing is trustworthy: a classifier over the surrounding language, not the identifier alone, applied the same whether that language was spoken on a call or typed in a thread. “Blocked by NOD-231” chains the record in; “Closing as duplicate of NOD-231” keeps it out. Identical identifier, opposite relationship. Naive extraction - regex for anything shaped like a ticket ID - produces a graph so noisy it is worse than nothing, because a wrong edge in a customer graph costs more than a missing one.
Two things this design deliberately does not do. It does not move your data: the graph holds pointers, not copies, and records stay in the systems that own them, under those systems' permissions. And it does not replace your warehouse: aggregate questions belong in BI and always will. Snowflake and Looker answer what happened across the business; a context graph answers what's happening with this account right now, and why - a live, per-account, relationship-shaped question a star schema was never built to serve.
You could build all of it. If you do, build it honestly: connectors across the full action layer including email and doc comments, interaction rank over a continuously updating edge stream, a decay model tuned to your business cycle, an intent classifier with a labelled evaluation set so precision is proven rather than asserted, permission-aware resolution so the graph never becomes a lateral disclosure path, and a cached serving layer or token spend will sink the business case on its own. That is a real platform team and a real multi-quarter programme.
If you'd rather have it running than build it, that is what Noded is. We read the action layer rather than the storage layer, assemble a live context graph per account from pointers rather than copies, and expose it to Claude, ChatGPT, Gemini or your own agents through one governed MCP layer. Data stays in the systems that own it; write-backs are opt-in, attributed and reviewable; nothing is ever trained on. SOC 2 audited.
A platform decision at this layer doesn't get made from a free trial, so we haven't built the ask around one. The next step is a working session: we walk your architecture, show you what ingestion chaining surfaces on an environment shaped like yours, and take the security and governance questions before anything is connected to anything.
Either way, run the diagnostic first. One account, thirty artifacts, an afternoon.
Because the relationship isn't in the data. It's in the work - and the work is already written down.