The hierarchy and the people
A district is a shape, and the shape is the product.
The thesis is on the front page: an object model is an argument about the world. This page is the argument worked out in detail — how a school joins the national directory, how the district above it gets resolved instead of typed, and why one contact per account is where district selling goes wrong.
Every claim here names the file it came from, and where the rule is load-bearing, the line in it.
The thesis
An object model is an argument about the world.
Software people tend to treat a data model as plumbing. It is not. A data model is a claim about which things exist, which of them are the same kind of thing, and what is allowed to happen between them — and once it ships, everyone who uses the software has to believe that claim in order to get through the day.
The general-purpose CRM makes a coherent and useful claim: there are companies, companies have employees, and money moves when a company decides to buy. Nearly all business-to-business selling really does look like that, which is why the category is enormous and mature and worth buying.
Selling into a school district does not look like that. The entity you serve is a school; the entity that signs might be the district; the person who cares most is a teacher whose actual job is teaching; the budget for the yearbook, the budget for picture day, and the budget for the booster club’s sponsorship page are three different budgets with three different custodians; the calendar is a school year; and the thing you are competing against is usually an incumbent publisher on a multi-year contract that ends on a date somebody wrote in a filing cabinet. None of that is exotic. It is just not what the model was built for.
So this is the translation, line by line. The left column is the shape every general CRM ships. The middle column is the shape already in this platform’s schema. The right column is where to find it, because a claim without a location is a slogan.
| The general shape | The shape a district actually has | Source |
|---|---|---|
| Account — a company you typed in | A school, joined to the national school directory’s own row for it, and to the district above it, and to the workspace it becomes once you win. | crm-account.ts:8 |
| Parent account — a field somebody fills in | The district, RESOLVED from the directory’s own local-education-agency identifier. Nobody types it, so nobody mistypes it. | crm-account.ts:141 |
| Contact — an employee of the account | Several named people per school, each with a role that means something different: the adviser who runs the book, the administrator who signs, the booster officer with a separate budget, the business office that pays. | org-prospect-crm.ts:328 |
| Deal — a row that turns green | The picture-day contract or the ad and sponsorship buy. Winning it PROVISIONS the customer, and a provisioned deal is then immutable. | crm-deal-engine.ts:384 |
| Lead score — employee count and industry code | Title-I status, enrollment band, how switchable the incumbent publisher is, how close the contract is to renewal, and whether the school is in your territory at all. | crm-deal-engine.ts:307 |
| Territory — a filter on a list view | States and geographic zones with per-account resolution, district-level assignment, and a decision record on a disputed claim. | org-territory.ts:584 |
| Products — a price list you keep in step by hand | Line items that reference a real plan or SKU, whose price is re-read from the catalog and whose client-supplied price is discarded. | crm-deal-engine.ts:414 |
| Close-won — a stage name | A conversion that stands up a school’s own workspace. The stage-move validator refuses to move it afterwards, by design. | org-prospects.ts:302 |
| Proof of delivery — an attachment | A tear sheet: refused until the invoice settles, and refused again if any student on the placement is publication-suppressed. | ad-crm.ts:3181 |
The hierarchy nobody should have to type
The school is the account. The district above it is resolved, not entered.
In a general CRM, an account hierarchy is a field. You look up the parent, you select it, and from then on the accuracy of every district-level roll-up depends on a human having done that correctly a few thousand times. The failure is silent and it is cumulative: a district total that is quietly missing four schools looks exactly like a district total that is right.
Here the school’s row in the national school directory already carries its local-education-agency identifier — and in that data, the local education agency is the district. So the roll-up walks upward from a fact rather than from an entry: take this account’s directory row, read its agency identifier, find every sibling school under the same one, and sum the deal value across the ones your studio actually has an account for.
Three properties of that sum matter more than the sum.
It is your data only. The aggregate is built from your studio’s own accounts and your own deals. A sibling school in the district that you have no account for contributes zero — not an estimate, not an industry average, not a placeholder. Zero, because we do not know.
A missing district is an answer, not an error. The roll-up has four responses and it names them: the account is not linked to a directory row yet; the link exists but the row behind it does not read; the school has no district on file at all; or the district is resolved and here it is with its siblings. The third one is the interesting one. A great many private and independent schools genuinely have no district, and calling that “unavailable” would be a lie of the most ordinary kind — the kind where the software cannot tell the difference between a fact it knows and a fact it is missing.
A mis-linked deal cannot leak. The link from an account to a deal is a plain identifier, and the patch endpoint will accept any well-formed one. So before the roll-up trusts a deal’s amount, it re-checks that the deal belongs to the studio doing the asking. That check is redundant if everything upstream is correct, which is precisely why it is there.
- no-directory — This account is not linked to a directory row yet. We cannot answer, and we say so.
- directory-unresolved — The link exists but the row behind it does not read. Still not an answer.
- independent — The school genuinely has no district on file. A real fact about many private and independent schools — a RESOLVED answer, not a gap. A resolved answer.
- resolved — The district identifier, every sibling school under it, and the summed deal value across the ones YOUR studio has an account for. A resolved answer.
And now the bound, in the same breath as the capability, because separating them is how a page becomes untrue. No screen asks this question today. The endpoint is written, registered and correct; there is no browser client for it at all. It is in the built-not-switched-on table on the what you can reach page, by name, with the measurement that established it.
The people
A school is not one contact with a job title.
The single-contact habit is where most district selling goes wrong, and it is not a data-entry problem. It is that the four people who decide whether you get the work have genuinely different relationships to it. The yearbook adviser has the strongest opinion and the least time; the administrator has the signature and the least context; the booster or activities officer has a separate budget and a separate reason to care; the business office has the payment terms and no interest in the product at all. A model with one contact per account forces a rep to pick which of those four the account “is”, and then to keep the other three in their own head.
So contacts are plural, per school, each with their own role, created and edited and removed through their own endpoints. Every touch appends to that school’s timeline. The stage-move endpoint writes a timeline entry itself and stamps the last-activity clock, so the quiet-deal detector has an honest clock to read rather than one that depends on a rep remembering to log a call they already made.
Two facts about this surface, because both are load-bearing. It is reachable: the prospect detail screen is linked from the pipeline board and drives the timeline and the contact model. And the whole thing carries adult organisation context only — a school’s staff and a business’s owner, never a student. That is not a policy we intend to follow; it is the shape of what the code reads and writes, and the route module states it in its own header.
One more thing worth saying because it is the kind of detail that tells you who built something. The spelling is adviser, everywhere, in the code and on this page. In scholastic publishing that is the word, and a product that keeps getting it wrong is telling you where it came from.
Territory
Territory is a set of rules with a decision record, not a filter on a list.
A studio with more than two reps has a territory problem within about a season, and it is rarely a technical one. Two reps work the same district from different angles; a school moves; a district consolidates; somebody claims an account that somebody else has been nursing for eight months. The software’s job is not to prevent that — it is to make the resolution legible after the fact.
So territories are defined as states and zones, accounts and whole districts can be assigned into them, and there is a per-account resolution endpoint that answers the only question that matters in an argument: whose is this one, and on what basis. Live territory search is registered and wired to the founder-facing territories screen.
Then the bounds, and there are several. Replacing a territory’s geographic zones has no browser client. Assigning a district to a territory has no browser client. Recording the decision on a disputed territory claim has no browser client. Two of the three map reads have no browser client — the founder map data is wired and those two are not. Four capabilities, written and registered and unreachable, all four in the table on the what you can reach page.
And one correction that runs the unflattering direction, kept because those are the ones that buy credibility. Three comments in our own web code still describe the live territory search endpoint as dark and not live yet. Measured at tip, it is registered and it is called. We under-sold a shipped capability in our own source for long enough that an audit had to find it, which is a small embarrassment and a large argument for measuring rather than remembering.