Sorts and Hierarchy
Sorts are the type system of the Reasoning Layer. They are not classes, categories, or tags. A sort defines a position in a lattice of types, with feature declarations and constraints that its instances (terms) must satisfy.
Why sorts instead of classes or tables?
Section titled “Why sorts instead of classes or tables?”If you come from object-oriented programming, you’re used to single-inheritance class hierarchies. If you come from databases, you’re used to flat tables with foreign keys. Sorts offer advantages over both:
- Multiple inheritance without the diamond problem: An
employeecan be both apersonand anorganization_member. In OOP, this causes ambiguity. In the sort lattice, the Greatest Lower Bound (GLB) resolves it mathematically. - Structural pattern matching: You can query for “anything that has a
namefeature and is a subtype ofentity” — the engine finds all matching sorts and their terms automatically. - Feature declarations with constraints: Each sort declares what features its terms can have, with type constraints, required/optional flags, and inter-feature ordering rules. This is richer than both OOP fields and SQL columns.
- Lattice operations: You can compute the most specific common type (GLB) or most general common type (LUB) of any two sorts — enabling the engine to reason about type compatibility automatically.
What is a sort?
Section titled “What is a sort?”A sort is a named type in a partially ordered lattice. Every sort has:
- A unique name within a tenant
- Zero or more parent sorts (multiple inheritance)
- Feature declarations describing what named attributes instances may or must have
- Bound constraints expressing ordering relationships between features
- A lifecycle status and origin tracking how the sort was created
The lattice structure means that any two sorts have a well-defined Greatest Lower Bound (GLB) and Least Upper Bound (LUB), enabling the system to reason about type compatibility.
Sort hierarchy as a lattice
Section titled “Sort hierarchy as a lattice”Sorts form a partial order — a directed acyclic graph where edges represent the subtype relation. Unlike single-inheritance class hierarchies, the sort lattice supports multiple parents:
entity / \ person organization \ / employeeIn this example, employee inherits from both person and organization. It inherits the feature declarations of both parents.
Creating sorts with SortBuilder
Section titled “Creating sorts with SortBuilder”The SortBuilder provides a fluent API for constructing CreateSortRequest objects:
import { SortBuilder, ReasoningLayerClient } from '@kortexya/reasoninglayer';
const client = new ReasoningLayerClient({ baseUrl: 'https://platform.ovh.reasoninglayer.ai', tenantId: 'your-tenant-uuid', auth: { mode: 'cookie' },});
// Create a base sortconst personSort = await client.sorts.createSort( SortBuilder.create('person') .feature({ name: 'name', required: true }) .feature({ name: 'age', required: false, constraint: { type: 'Integer' } }) .description('A person entity') .build(),);
// Create a child sort with multiple inheritanceconst employeeSort = await client.sorts.createSort( SortBuilder.create('employee') .parent(personSort.id) .feature({ name: 'salary', required: true, constraint: { type: 'IntegerRange', value: { min: 0, max: 10000000 } }, }) .feature({ name: 'department', required: true }) .boundConstraint({ constraintType: 'upper', target: 'end_date', sourcePath: 'company.dissolution_date', }) .description('An employee at a company') .build(),);Deterministic UUIDs
Section titled “Deterministic UUIDs”You can set a deterministic UUID for cross-referencing sorts with rules and facts in homoiconic systems:
const sort = SortBuilder.create('my_sort').withId('550e8400-e29b-41d4-a716-446655440000').build();If omitted, the server generates a UUID automatically.
Bulk sort creation
Section titled “Bulk sort creation”When defining an entire hierarchy at once, use bulk creation with name-based parent references (not UUIDs). The server resolves names to UUIDs:
const result = await client.sorts.bulkCreateSorts({ sorts: [ { name: 'entity', description: 'Base entity' }, { name: 'person', parents: ['entity'], features: [ { name: 'name', required: true }, { name: 'age', required: false }, ], }, { name: 'organization', parents: ['entity'], features: [{ name: 'org_name', required: true }] }, { name: 'employee', parents: ['person', 'organization'], features: [{ name: 'salary', required: true }], }, ],});
// result.sortIds: Record<string, string>// e.g., { "entity": "uuid1", "person": "uuid2", ... }// result.createdCount: 4Feature declarations
Section titled “Feature declarations”Each sort declares features using FeatureDescriptorDto:
const descriptor: FeatureDescriptorDto = { name: 'salary', required: true, expectedSort: 'number', // expected sort name for the value expectedTypeHint: 'Integer', // hint for LLM extraction constraint: { type: 'IntegerRange', value: { min: 0, max: 10000000 }, },};Available constraint types include: Integer, String, Boolean, Real, IntegerRange, StringPattern, Enum, DateRange, TimestampBefore, TimestampAfter, PathEquation, FuzzyRange, FuzzyLinguistic, FuzzyPathSimilarity, PathLessThan, and PathLessOrEqual.
Bound constraints
Section titled “Bound constraints”Bound constraints express ordering relationships between features across a sort’s instances:
const constraint: BoundConstraintDto = { constraintType: 'upper', // "upper", "lower", "less_than", "equal" target: 'termination_date', // feature to constrain sourcePath: 'company.dissolution_date', // dot-separated path to the bounding value onViolation: 'residuate', // "update", "fail", "warn", "residuate"};GLB and LUB
Section titled “GLB and LUB”The GLB (Greatest Lower Bound) is the most specific type shared by two types. The LUB (Least Upper Bound) is the most general type covering both.
// Compute GLB of two sortsconst glbResult = await client.sorts.computeGlb({ sort1Id: personSort.id, sort2Id: organizationSort.id,});// glbResult.glb is the unique GLB sort UUID; it is absent when the meet is// bottom or not unique. glbResult.maximalLowerBounds lists every maximal// common subsort (several entries: a non-unique meet; none: bottom).// lubResult (below) has the same shape: lub and minimalUpperBounds.
// Compute LUBconst lubResult = await client.sorts.computeLub({ sort1Id: employeeSort.id, sort2Id: managerSort.id,});
// Decode GLB as a type disjunction (when no single named sort exists)const decoded = await client.sorts.decodeGlb({ sort1Id: sortA.id, sort2Id: sortB.id,});// decoded.formatted: "int | real"// decoded.isBottom: false// decoded.sortIds: ["uuid1", "uuid2"]// decoded.sortNames: ["int", "real"]
// Or use the convenience aliases:const glb = await client.sorts.findCommonSubtype(personSort.id, organizationSort.id);const lub = await client.sorts.findCommonSupertype(employeeSort.id, managerSort.id);const decoded2 = await client.sorts.explainCommonSubtype(sortA.id, sortB.id);Navigating the hierarchy
Section titled “Navigating the hierarchy”// Direct relationshipsconst children = await client.sorts.getChildren(sortId);const parents = await client.sorts.getParents(sortId);
// Transitive relationshipsconst ancestors = await client.sorts.getAncestors(sortId);const descendants = await client.sorts.getDescendants(sortId);
const compatible = await client.sorts.getCompatible(sortId);
// Subtype checkconst isSubtype = await client.sorts.isSubtype(childId, parentId);// returns true if childId is a subtype of parentIdSort origin
Section titled “Sort origin”SortOriginDto is a tagged union (discriminated by "type") that records how a sort was created:
| Variant | Fields |
|---|---|
Manual |
createdBy: string |
LlmExtracted |
confidence: number, sourceDocument?: string |
Imported |
ontology: string, originalId: string |
System |
(none) |
Learned |
confidence: number, exampleCount: number |
AutonomousLearning |
confidence: number, observationCount: number, source: string |
Sort status
Section titled “Sort status”SortStatusDto is a tagged union tracking the lifecycle of a sort:
| Variant | Fields |
|---|---|
Active |
(none) |
Deprecated |
reason: string, replacement?: string |
Proposed |
submittedBy?: string |
Update a sort’s review status:
await client.sorts.updateReviewStatus(sortId, { approve: true,});SortDto reference
Section titled “SortDto reference”The full SortDto shape returned by the API:
| Field | Type | Description |
|---|---|---|
id |
string |
Sort UUID |
name |
string |
Sort name (unique within tenant) |
tenantId |
string |
Owning tenant UUID |
parents |
string[] |
Parent sort UUIDs |
featureDeclarations |
FeatureDescriptorDto[]? |
Feature descriptors |
boundConstraints |
BoundConstraintDto[]? |
Inter-feature ordering constraints |
description |
string? |
Human-readable description |
origin |
SortOriginDto? |
How this sort was created |
status |
SortStatusDto? |
Lifecycle status |
needsReview |
boolean? |
Whether human review is needed |