field-note11 min read
How does an institution remember?
Last note I was building the company operating system and the first pieces work. They are also empty. This is the problem underneath that: almost no company has one canonical list of the people it knows, and when a person leaves, the relationships leave with them.
Every company I have worked with runs on one sentence.
Who is our contact at the ministry. Ask Sarah. Who did we buy the generator from. Ask Sarah. Who was that person from the foundation who wanted to talk about education. Ask Sarah, she knows the guy.
That is not an institution. That is one person’s memory doing a job the company thinks it is doing itself.
And it holds up until Sarah leaves. Then fifteen years of relationships walk out with her: not only the clients, but the supplier who gives you credit, the person who fixes things at 11pm, the official who actually answers. Nobody notices any of it is gone until the day they need it.
I know exactly how this ends because I have already lived it. In 2021 Senpai had two thousand community members, four cohorts, and graduates earning in dollars. I could not tell you who any of those two thousand people were. The relationships existed. The record did not, so when I stopped, they stopped.
Last note I put the first working pieces of the demand system live. The index runs, the pipeline runs, the diagnostic captures people, and the score moved to 31.
The live system, this morning. The index is real and the pipeline is empty.
Then I opened the contacts screen.
Nine years of relationships, and this is the record of them.
So this is the problem I am on now, and I am writing it before the build starts. The targets are at the bottom, along with the score I am predicting. If I am wrong about them, note 05 says so.
Why the list is the whole of demand
The cheapest sale is to somebody who already knows you. Every business book says it and almost nobody here can act on it, because acting on it means producing the list and the list does not exist.
But the asset is not the name and the email. It is the history stuck to them. When we met. What they asked for. What we quoted. Why it died. Who introduced us. That is what makes a message two years later land as a continuation. Without it you pay to acquire the same people over and over, forever.
There is a second reason, and for an owner it is the one that actually frightens. A company that cannot list the people it knows cannot survive the people who know them leaving. Continuity is the product here, and demand is the thing it happens to unlock first.
What the CRMs actually do
I spent a while in the schemas before writing anything, because “build a CRM” is not a design.
| How it models a person | Catching duplicates | |
|---|---|---|
| Salesforce | Account, Contact, and Lead as a separate object | Manual rules, plus a paid layer |
| HubSpot | Company, Contact, and lifecycle stage as a field on the contact | Email, automatic |
| Pipedrive | Organization, Person, Deal | Loose |
| Attio | Objects you define, built out of your mail and calendar | Strong |
| Odoo, ERPNext | One party table holding customers, suppliers and staff alike | Reasonable |
The Salesforce row explains the whole industry. A Lead is not a Contact. It is a separate object holding a separate copy of a person, sitting in a parallel universe until somebody hits Convert, at which point the system creates an Account, a Contact and an Opportunity and retires the Lead. The same human can exist twice, in two tables, with two histories. That is the schema working as designed. Every duplicate-management product in that ecosystem exists to clean up a mess the data model makes on purpose.
HubSpot rejected that and made the contact one record with lifecycle stage as a property on it. Closer to what I want. The limit is that lifecycle stage holds one value, so a person cannot be your supplier and your prospect at the same time, and the record is still fundamentally a sales object.
The honest finding is the last row. One register for everybody the business knows is the ERP tradition, not the CRM tradition. Odoo has one party table for customers, suppliers and employees. So the idea is not new. It already exists, inside software that a nine-person business in Abuja finds unbearable to run.
What I built differently, and why
Four things. I would not claim more than four.
A lead is a state, not a person. One row per human, ever. A lead is a pipeline state pointing at that human. There is no Convert button because there is nothing to convert. Somebody who ran the diagnostic in August and turns up in my DMs in November is one contact with two interactions, and “where did this person first hear about us” has an answer.
The source is written once and can never be overwritten. Attribution that records the last touch is worse than no attribution, because it is confidently wrong.
A contact does not need a commercial reason to exist. Everyone who finishes the diagnostic becomes a contact. Only somebody who says they are an organisation becomes a lead. In a CRM you create a record because money is in play, which is precisely why the supplier and the person who fixes the generator never make it in, and why the company list is always the sales list wearing a bigger name.
It lives in the same database as everything else. The index, the diagnostic, the pipeline and eventually delivery and money all point at one contacts table. This is why I stopped arguing about integrating a CRM. An operating system that keeps its customers in somebody else’s database has given up the thing it exists for.
Where we are behind, plainly: no mail sync, no calendar sync, no enrichment, no merge screen. HubSpot dedupes on email for free. Attio builds your whole register out of your mailbox while you do nothing. They have ten years of inlets. I have a schema and one form.
Then the actual problem: it is empty
A register nobody fills is a dead address book. Every attempt at this dies the same way, and what kills it is effort.
Two kinds of density, and only one is worth having.
Breadth is many rows. A LinkedIn export gives you two thousand this afternoon. Depth is history attached to those rows, and it is the entire value.
Two thousand contextless names is not a dense register. Ask it who you should talk to and it returns noise, you stop trusting the answer, and the habit dies in a fortnight. So the bar for anything entering is one row plus at least one true fact about the relationship. Where we met, what they do, why they matter. “Community member, joined 2019, attended Design 101” clears that bar. Thin and true beats rich and invented.
Which inverts the obvious plan. My two thousand community members are the biggest number and the worst import. The seventeen Abuja ecosystem bodies I researched last week are a small number and the best one, because every one of them arrives with its history already written.
The part I think is genuinely interesting
Without a machine, an inlet has to produce clean structured data. That means forms, fields and typing, which is exactly the effort that kills the register.
With one, the bar for what counts as an inlet collapses. A research note is an inlet. A block of messy text is an inlet. A forwarded email, a WhatsApp export, a conference programme, an email signature. All inlets.
So intelligence is not the reward sitting downstream waiting for density. It is the thing that produces density. The clever querying comes second and mostly for free.
Which gives one build worth more than the rest of them combined. One box.
Paste anything. We will figure out the rest.
You dump in the email somebody sent you after a conference. It comes back with: I found three people and two organisations. Acme Foundation already exists. This John Smith is probably the John Smith you know. Michael’s role appears to have changed. Confirm all, or review individually.
Every integration becomes optional at that point. Connect your Gmail, connect your WhatsApp, import a CSV: all still useful, none of them a prerequisite. The universal API is paste it here.
The machine does not get to decide
Here is the line I care most about, and it produced a new value in our ethos while I was designing this.
RAW the source, kept forever
↓
EXTRACTED a machine's claim
↓
MATCHED resolved against what we already have
↓
PROPOSED a new fact, or a change to an existing one
↓ ↘
↓ REJECTED remembered, never proposed again
↓
CONFIRMED a human said yes. actor and timestamp
↓
...and it ages visibly until somebody checks again
The agent translates. The institution decides. An agent can read a mailbox, a research note or a conference programme and put forward what it thinks it found. It does not get to decide that Senpai now knows it. That gap is the whole difference between institutional memory and a company quietly filling with plausible fiction, and it matters more every year that more of the reading is done by machines.
Three rules make it work.
The agent extracts, plain code matches. A model turns mess into candidates. Deterministic code decides whether a candidate is somebody we already know: exact email, exact phone, normalised name and organisation. I will never ask a language model whether two records are the same person. That step has to be reproducible.
No confidence percentages. An 82% out of a model is theatre unless something calibrated it, and nothing did. Three buckets: exact auto-confirms, likely is pre-selected for review, possible is shown unselected.
Adding is cheap, overwriting is not. A new contact can auto-confirm. Two records being the same person, a role change, an organisation change, anything replacing an existing value, always needs a person. That line is what stops the review queue becoming the new friction.
And rejections get remembered. When I say no, that no is recorded and the same proposal never comes back from the same source. A system that forgets its corrections gets switched off within a month.
What I am not building
Writing this down so the scope cannot creep back in.
No new object types for projects, events or funding rounds. No relationship strength scores, which are invented numbers and break a value we already hold. No opportunity detection out of pasted proposals. No direct integrations with Gmail or WhatsApp, because the paste box makes every one of them an optimisation of a working thing.
The targets, set before the build
Four things have to be true or it is not finished. A register holding real relationships. A person can be added by hand in under thirty seconds. Adding somebody already in there does not create a second row. Every row carries a source that cannot be overwritten.
The thirty seconds is a real metric. Longer than that and nobody maintains it.
Then five questions, and the register is done when it answers all five without me opening anything else:
- Who do we know at a government body?
- Who was the last person we spoke to at organisation X, and when?
- Who asked about pricing and never got a follow-up?
- Where did each of our last ten contacts come from?
- Which organisations are partners, which are buyers, and has any been both?
Volume floor: thirty contacts across twenty organisations, every one with depth. Small on purpose.
And the prediction, because a score you write after the fact is not a measurement. This gets the contact capability to built, not running. It will be deployed, real and usable, and it will still be me filling it. It reaches running when contacts arrive without me sitting down to do data entry, which is a fact about the inlets and not about the schema.
The number that decides it is contacts added per week that nobody typed. Zero for two weeks means the inlets failed and the score does not move.
The build order
Five steps, and the order is the argument.
01. The schema and the matching engine. Sources and claims as their own tables. The engine that decides whether a candidate is somebody we already know holds no database connection, so the matching rules can be tested without one.
02. The form. Add a person by hand in under thirty seconds. A human typing into a form is the confirmation, so it writes straight to the register with no queue and no proposals. That step alone satisfies everything I called done above.
03. Forty rows, typed by me. The seventeen Abuja bodies, the organisations I researched this month, real clients, real suppliers. Then I run those five questions against it. If it cannot answer them at forty rows it will not answer them at four hundred, and that is worth finding out before building anything clever.
04. The paste box. Only now, because this is the first point where a machine starts making claims, and there is no sense building the confirmation chain before there is anything to confirm.
05. Four years of old mail. Google does not delete anything, and I am fairly sure it is all still sitting there.
Steps 1 to 3 are a day and a half and produce a register that answers real questions. Step 4 is the interesting one and it is also where a week disappears, which is exactly why it is fourth.
Come back in a month and check whether the register has anything in it, and whether I put it there by hand.
Stay updated
We are building the digital infrastructure Africa needs, and publishing the whole thing as it happens. Field notes from inside the work, research, case studies, and the decisions that turned out wrong. Roughly weekly.
No pitch, and one click to leave.
SENPAI is a social impact design and technology company in Nigeria. We publish what we learn from the work, including the parts that did not go to plan.
Bring us a problem