Skip to content

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 employee can be both a person and an organization_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 name feature and is a subtype of entity” — 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
\ /
employee

In 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 sort
const 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 inheritance
const 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: 4

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

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 sorts
const glbResult = await client.sorts.computeGlb({
sort1Id: personSort.id,
sort2Id: organizationSort.id,
});
// glbResult.glb is the GLB sort UUID, or null if none exists
// Compute LUB
const 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);
// Direct relationships
const children = await client.sorts.getChildren(sortId);
const parents = await client.sorts.getParents(sortId);
// Transitive relationships
const ancestors = await client.sorts.getAncestors(sortId);
const descendants = await client.sorts.getDescendants(sortId);
const compatible = await client.sorts.getCompatible(sortId);
// Subtype check
const isSubtype = await client.sorts.isSubtype(childId, parentId);
// returns true if childId is a subtype of parentId

Sort origin

SortOriginDto is a tagged union (discriminated by "type") that records how a sort was created:

VariantFields
ManualcreatedBy: string
LlmExtractedconfidence: number, sourceDocument?: string
Importedontology: string, originalId: string
System(none)
Learnedconfidence: number, exampleCount: number
AutonomousLearningconfidence: number, observationCount: number, source: string

Sort status

SortStatusDto is a tagged union tracking the lifecycle of a sort:

VariantFields
Active(none)
Deprecatedreason: string, replacement?: string
ProposedsubmittedBy?: string

Update a sort’s review status:

await client.sorts.updateReviewStatus(sortId, {
approve: true,
});

SortDto reference

The full SortDto shape returned by the API:

FieldTypeDescription
idstringSort UUID
namestringSort name (unique within tenant)
tenantIdstringOwning tenant UUID
parentsstring[]Parent sort UUIDs
featureDeclarationsFeatureDescriptorDto[]?Feature descriptors
boundConstraintsBoundConstraintDto[]?Inter-feature ordering constraints
descriptionstring?Human-readable description
originSortOriginDto?How this sort was created
statusSortStatusDto?Lifecycle status
needsReviewboolean?Whether human review is needed