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?
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?
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
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
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
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
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
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
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
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 GLB sort UUID, or null if none exists
// 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
// 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
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
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
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 |