Add an ontology entity type or field
The ontology is the contract every other subsystem depends on, so changes here ripple — but in a controlled, validated way. This guide covers adding a field to an existing type (common, easy), adding a whole entity type (rarer, touches resolution), and adding a graph relationship. Read Ontology and Entity resolution first.
Add a field to an existing type
This is the common case (it's how country_code, lei, vat_number were added in v3.5.2).
- Edit
schemas/ontology/ontology_v3.5.yaml— add the attribute under the entity type, with atype, optionalrequired, and asurvivorshipstrategy:LegalEntity:attributes:my_new_field:type: stringsurvivorship: most_trusted - Add provider trust for the new field under
survivorship.provider_trust.<provider>so survivorship knows how much to trust each source for it. - Map it in any provider's
mapping_spec.yamlthat can supply it (see Add a provider). - Version & migrate. Minor bumps keep the filename; add a companion migration
(
migrations/V###__ontology_schema_v3_5_x.sql) that publishes the YAML byte-equal intoontology_schema_versions. A CI drift check enforces YAML↔DB parity.
Add a new entity type
A new type touches resolution because Atlas must know how to match it. Files to touch:
| File | Change |
|---|---|
schemas/ontology/ontology_v3.5.yaml | Define the entity_type with attributes |
src/ontology/registry.py | Register the type's schema/class |
src/ontology/entity_matcher.py | Add a _match_<type>() and dispatch it in generate_matching_key() |
src/ontology/blocking.py | Add blocking-key generation for the type |
src/ontology/reconciliation.py | Handle the type in _entities_match() / clustering |
Design the matching key carefully. Atlas's resolution quality comes from good keys: a strong identifier (registration number, normalized) gives a high-confidence key; a name-only key is weaker. Follow the existing pattern — identifier-first with a normalized-name fallback, and a blocking key that's cheap enough to group on. See the concrete thresholds in Entity resolution.
Add a graph relationship
Relationships are schema-driven — the Cypher is generated from the ontology, nothing is hard-coded.
- Declare it under
relationship_typesin the ontology YAML, including aneo4j_type:relationship_types:MyRelation:source: LegalEntitytarget: Personneo4j_type: "MY_RELATION" - The
CypherGeneratorreadsneo4j_typeautomatically (falling back to upper-snake-case), andNeo4jSyncServicewill project instances during graph sync. Every generated query includestenant_idin MERGE/MATCH keys for isolation. - Query it:
MATCH (le:LegalEntity {tenant_id:$tenant_id})-[:MY_RELATION]->(p:Person {tenant_id:$tenant_id})WHERE le.id = $entity_id RETURN p
Invariants
- Determinism: a given entity must always produce the same matching key.
- Protected fields: if your new field is a screening signal, add it to
protected_fieldsso providers can't overwrite it. - Parity: the YAML and the
ontology_schema_versionsrow must match — CI enforces it.
Next: Add a risk matrix to score on your new field.