The atomic unit a specification defines — the noun products pass around and compose with.
A primitive is the foundational compositional unit a specificationSpecificationFoundationsA specification entityView reference → defines: the atom products create, exchange, compose, and transform. Where a specification is the contract, a primitive is what the contract is about. The Portable Text specification defines blocks, spans, and marks. The GraphQL specification defines scalars, objects, and interfaces. A primitive is the noun; the specification is the grammar.
The word primitive entered computer science through programming language theory, where primitive types (integers, booleans, characters) are the atoms the language defines directly, as opposed to compound types the programmer composes from them. The distinction between a primitive and a composite shows up everywhere a type system exists: in SQL (VARCHAR, INTEGER vs. a table row), in JSON Schema (string, number vs. an object), and in every API contractAPI ContractEngineeringAn API contract or specificationView reference → that names its first-class objects.
At the application layer, the idea carries forward into protocol design. REST popularised the resource as the first-class noun an API exposes. Event-driven architectures name their events as the atoms that flow between services. Domain-Driven Design calls them value objectsValue ObjectEngineeringA DDD value objectView reference → and entities. In every case the underlying insightInsightUser ResearchA synthesised finding from researchView reference → is the same: name the atoms before you name the operations, because the atoms are what interoperability is built on.
In UPG the primitive entity was introduced alongside specification to close a modelling gap. Before it, a product that implemented GROQ had a featureFeatureProduct SpecificationA product capability or featureView reference → node for GROQ support, but there was no way to represent the GROQ expression, operator, or result type as a named thing that multiple features across multiple products pass around. The primitive entity names those atoms, links them to their governing specification, and makes their composition explicit in the graph.
A content platform builds three features that all operate on Portable Text: a rich-text editor, a plain-text export utility, and a search indexer. Without primitive nodes, each feature node implicitly assumes a shared understanding of what a "block" and a "span" are, but that understanding is nowhere in the graph. Two teams can drift on the schema without the graph revealing the inconsistency.
With primitive nodes, the team creates block, span, and mark nodes linked to the Portable Text specification via Primitivedefined bySpecificationsemantic. Composition is modelled via primitive_defined_by_specificationPrimitivecomposesPrimitivehierarchy (block contains span, block contains mark). Each of the three features declares which primitives it consumes. A graph query for "all features that operate on the span primitive" returns all three, surfacing the shared dependencyDependencyTeam & OrganisationA cross-team or system dependencyView reference → that was previously invisible.primitive_composes_primitive
In the Unified Product Graph a primitive is a lifecycle-free entity in the Foundations domain, modelled after the same pattern as metricMetricStrategyA unified metric that measures progress, health, or behaviour across the productView reference →: it exists or it is deprecated; there is no draft-active-shipped progression. Its primary edge is Primitivedefined bySpecificationsemantic, which links the atom to its governing contract. Composition is modelled via primitive_defined_by_specificationPrimitivecomposesPrimitivehierarchy, allowing the graph to represent nested or container structures (a block contains spans). Like specification, a primitive is registry-hostable: one canonical block node can be shared by every product that implements Portable Text, rather than each product inventing its own representation.primitive_composes_primitive
The primitive_kind field classifies the atom: data_type for scalars and structured types, object for named record types, block for container units, and unit for indivisible atoms. A primitive that belongs to no governing specification is still valid — it represents an internal platform primitive that the team has named but not (yet) standardised.
Type-specific fields on BaseNode
primitive_kindenumThe shape of the thing products pass around.
defined_bystringThe `specification/<slug>` this primitive comes from; nullable for spec-less internal primitives. Mirrors the `primitive_defined_by_specification` edge for quick lookup.
sincestringYear or version the primitive was introduced.
idstringrequiredUnique identifier (UUID)
typeNodeTyperequiredDiscriminator for the entity type
titlestringrequiredDisplay name
descriptionstringOptional detailed description
statusstringLifecycle status
tagsstring[]Freeform tags for filtering
3 edge types connected to this entity.
primitive_composes_primitiveprimitive_composes_primitiveprimitive_defined_by_specification