Four Things That Exist Afterwards
Adoption stories usually get told as a list of features. This one is easier to tell as an inventory: after a team wires CoreModels to their Neo4j instance, four artifacts exist that did not exist before, and almost everything that changes about the week is downstream of those four.
Four Things That Exist Afterwards
Adoption stories usually get told as a list of features. This one is easier to tell as an inventory: after a team wires CoreModels to their Neo4j instance, four artifacts exist that did not exist before, and almost everything that changes about the week is downstream of those four.
- A governed model of the graph — labels as Types, properties as Elements, outgoing typed relationships as references — that a person can open and read.
- An audit report that compares a fresh export of the graph against that model and returns counts, coded findings, and a markdown write-up.
- A status badge that renders the last recorded audit run as green, yellow, red, or gray.
- A Cypher constraint script, generated from the model, that makes the database enforce what the model says.
Here is what having them does to an ordinary week.
The import that starts it
One call, once: POST https://coremodels.example.com/graph/integrations/neo4j/import/{projectId}
with Authorization: Bearer $TOKEN and the two artifacts you extracted — meta_schema from
CALL apoc.meta.schema(), and optionally constraints from SHOW CONSTRAINTS. What comes back is
a receipt rather than a shrug: datasetsAdded, datasetsSkippedExisting, fieldsAdded,
nodesEnriched, snapshotStored, plus lossiness and errors arrays. A first import of a modest
graph might report six datasets added, carrying thirty-nine fields between them — fieldsAdded
itself reads zero on a first import, because it counts only fields added to already-governed labels
on a re-import.
Behind those counts, uniqueness and existence facts land where a reviewer expects them: an
existence flag becomes NotNull, a unique flag becomes Unique, and SHOW CONSTRAINTS rows refine
both — a node-key constraint contributes uniqueness and existence to the property it covers. Node
counts from the sampled schema ride along as metadata. Relationship-type entries in the APOC output
are counted and skipped, because they duplicate what the node-side view already carries.
Tuesday, 09:40 — the pull request
A change to the ingestion job lands. CI extracts a fresh meta_schema.json against the staging
instance and posts it to the machine-to-machine surface:
POST https://coremodels.example.com/v1/{projectId}/integrations/neo4j/audit, authenticated with a
user API key rather than a login token. The response is wrapped in the standard envelope, so the
gate reads data.errorCount — greater than zero fails the build — and the job writes
data.markdown into the step summary so the author sees the report without opening another tab.
This particular PR retyped a property. The audit reports it under Drift with the code
field-type-drift, the subject names the exact field, and driftedObjects lists it for anything
downstream that wants to react. Before, that change would have merged and surfaced weeks later as a
comparison quietly returning fewer rows.
Tuesday, 11:15 — meaning moves, not the graph
A modeling review decides an identifier that has always been "usually unique" should be governed as unique. Someone makes that change in CoreModels — deliberately, by hand, because imports and audits never change meaning on their own.
Then the natural question: does the graph we already have still conform to the model we just
tightened? That is one call with an empty {} body:
POST https://coremodels.example.com/graph/integrations/neo4j/reaudit/{projectId}. It re-runs the
same audit engine over the schema snapshot stored at import time, against the current governed
model. No fresh export, no database access, nobody paged. The run always lands in the history,
because a drift trail with gaps is not a trail.
Tuesday, 14:00 — someone asks the model instead of the graph
An engineer, mid-review, wants to know whether the OWNS relationship is governed to one target
label or several. Rather than sampling nodes, they ask an assistant connected to
https://coremodels.example.com/mcp over OAuth. get_vendor_integration_status with
vendor: "neo4j" reports the last-import state; get_project_summary and search_nodes return the
governed types and elements. The answer arrives with its caveat intact: the reference is governed to
its first target, and the other labels are recorded.
Tuesday, 16:30 — the loop closes
The team wants Neo4j itself to enforce Tuesday's decision, so they call
POST https://coremodels.example.com/graph/integrations/neo4j/generate/{projectId} with
{ "typeNames": [] } for everything eligible. Back comes one artifact,
coremodels_constraints.cypher: CREATE CONSTRAINT ... IS UNIQUE for governed unique properties,
CREATE CONSTRAINT ... IS NOT NULL for governed NotNull properties (with a header note that
property-existence constraints require Enterprise), and CREATE INDEX for properties governed by a
controlled vocabulary. Every statement is IF NOT EXISTS, so the script is safe to run twice.
Reference elements are excluded by design — in Neo4j they are relationships, not properties.
The script goes through review like any other migration. Its header says, in the file itself, that meaning changes belong in CoreModels and the script should be regenerated rather than edited.
Friday — the badge
GET https://coremodels.example.com/v1/{projectId}/integrations/neo4j/badge returns
image/svg+xml — the route is authenticated, so a pipeline job fetches it with an API key and
publishes the file where the README can reference it. Green means the last recorded run was clean; yellow means
warnings; red means errors; gray means nothing has been recorded yet. It is a small thing that ends
a large category of conversation.
Before and after
| Before | After | |
|---|---|---|
| Where the schema lives | loaders, queries, memory | a governed model with an owner |
| How drift is found | a wrong query result | errorCount on a pull request |
| Constraints | whatever someone remembered | generated from governed meaning, reviewed like code |
| Onboarding question | "sample some nodes" | open the model |
| Agent behavior | guesses structure | consults it over /mcp |
| Credentials we hold | — | still none |
That last row is not a joke. There is no live Bolt or HTTP connection in this integration; the capability is declared and deliberately deferred. Everything above runs on two files you exported yourself.
What the after-state actually feels like
Boring, mostly. The questions that used to require an archaeology session — is this property required, is that key unique, which label does this relationship really point at, has anything changed since the last release — become lookups. The dramatic version of graph governance is an incident review. The undramatic version is a green badge, a clean audit on a Tuesday PR, and a constraint script that someone reviewed on Thursday afternoon.
Our Neo4j quickstart shows the same path — export, import, audit, re-audit, badge, generate — as concrete calls you can run today.