EKOSdocs
Docs / Integrations Build

LinkML

How EKOS turns recovered business semantics into a LinkML schema, and how an expert's edits to that schema come back as review decisions.

LinkML is where EKOS's business semantics leave EKOS. EKOS reads the traces business meaning leaves in code and history: filters, CHECK constraints, lookup rows, column comments and commits. It turns them into hypotheses, a person reviews them, and EKOS writes the result as a draft LinkML schema.

EKOS feeds LinkML; it does not replace it. EKOS writes one schema file and stops. JSON Schema, SQL DDL, Pydantic models, RDF/OWL/SHACL and data validation are left to LinkML's own generators.

The path end to end

Step Where What happens
1. Record traces ekos recover Every comparison of a column with a fixed value, such as entity_class IN (2) or NOT p.obsolete, is recorded with its file and line. The traces come from SQL views, routines and triggers, dbt models, Pentaho filters and Perl SQL strings.
2. Synthesise ekos commit with [semantics] enabled = true The traces, plus CHECK constraints, seed rows, column comments, constants, glossaries and git blame, become BusinessConcept, EnumMeaning, ConstraintCandidate, SemanticGap and RationaleLink items. Every item is a hypothesis with path:line evidence. No model is called unless llm-definitions = true.
3. Review ekos semantics confirm|reject|edit, or the console Only a person can confirm, reject or rename an item. No MCP tool can do it.
4. Export ekos export linkml The reviewed items become a LinkML schema. By default only confirmed items are exported.
5. Edit and import (optional) ekos import linkml An expert edits the YAML file. EKOS turns the edits into the same review decisions as step 3.
6. Generate LinkML tooling linkml-lint, gen-json-schema, gen-pydantic, linkml-validate and the other LinkML generators.

Export

ekos export linkml --out schema.yaml                  # confirmed items only (the default)
ekos export linkml --status hypothesis --out draft.yaml
ekos export linkml --status all --out all.yaml        # everything except rejected items
ekos export linkml --name shop --json                 # JSON instead of YAML

--name sets the schema's name and its id, https://w3id.org/ekos/<name>. It defaults to the workspace directory's name. The schema imports linkml:types and has default_range: string. Its ekos_export_status annotation records which status filter produced it.

The default --status confirmed means a hypothesis never leaves EKOS looking like a fact by accident. If the filter leaves nothing to export, the command fails and says how many items of each status the ledger holds.

How EKOS objects map to LinkML

EKOS LinkML
A Table that an exported item refers to a class with one attribute per column. The range comes from the SQL type: boolean, integer, decimal, float, datetime, date, time, otherwise string. NOT NULL makes the attribute required, and a one-column primary key makes it the identifier. The class is annotated ekos_status: observed, because it is structure read from DDL, not a guess.
BusinessConcept a class with is_a set to its table's class. Its description is the reviewer's description if there is one, otherwise the predicate. The predicate itself is always kept in ekos_definition.
The EnumMeanings of one column an enum whose permissible_values are keyed by the stored code (A, 2), with the label as the description
ConstraintCandidate slot_usage on the table class: minimum_value, maximum_value, pattern (a SQL LIKE becomes an anchored regular expression) or range. When a column's CHECK is confirmed, the column's range becomes its enum.
Open gaps and conflicts comments on the element, such as GAP: What does … and CONFLICT: …
Ontology suggestions ekos_suggested_exact_mappings and ekos_suggested_close_mappings annotations. These are never LinkML exact_mappings, because a suggestion is not a decision.

Annotations

Every recovered element carries ekos_* annotations, so the schema can always be traced back to its evidence:

Annotation Meaning
ekos_id the item's stable id; ekos import linkml matches on it
ekos_status confirmed, hypothesis, needs_review or observed
ekos_confidence how strongly the traces support the item
ekos_definition the predicate that defines a concept
ekos_evidence path:line references to the source
ekos_rationale the commits that last changed those lines
ekos_source where a code's label came from (column_comment, seed rows, a constant, …)
ekos_reviewed_by who confirmed it

Here is a confirmed concept and a confirmed enum from the LedgerSMB demo:

classes:
  CustomerAccount:
    is_a: EntityCreditAccount
    description: A credit account held by a customer (entity_class 2).
    annotations:
      ekos_id: 074fe04f-ed9b-548d-9201-6bc644be2ea6
      ekos_status: confirmed
      ekos_definition: entity_credit_account.entity_class IN (2)
      ekos_evidence: sql/modules/Company.sql:1297; sql/modules/Company.sql:1310; sql/modules/Company.sql:1397
      ekos_rationale: commit 1b5baf71b0 "Groundwork for fix on pricematrix issues" (2011-11-22); …
enums:
  AccountCategory:
    permissible_values:
      A:
        description: Asset
        annotations:
          ekos_status: confirmed
          ekos_reviewed_by: demo-reviewer
          ekos_source: column_comment
          ekos_evidence: sql/Pg-database.sql:72; …

Import: review inside the YAML

An expert who prefers to work in the LinkML file can review there and send the file back:

ekos import linkml schema.yaml --dry-run              # list the decisions the edits stand for
ekos import linkml schema.yaml --as ann               # record them, all or nothing
ekos import linkml schema.yaml --dry-run --json       # the same list as JSON

EKOS rebuilds the export the file came from, using the status filter in its ekos_export_status annotation, and compares the two element by element. Elements are matched by ekos_id, never by name, so a renamed class is still the same concept.

Edit in the YAML Review decision
a concept class renamed edit --name
a concept's description changed edit --description
a code's description changed edit --label
ekos_status: confirmed confirm
ekos_status: rejected, with an ekos_review_note reject
ekos_review_note changed recorded with the decision

Some edits are errors: - a renamed code (the code is what the database stores); - an ekos_id that EKOS does not know; - a rejection without a note.

Other edits are reported and not imported: new classes, deleted elements, slot changes and any other status. EKOS records decisions about what it recovered, nothing more.

Validation is all or nothing: if any element is in error, nothing is written. The YAML file is never a second source of truth; only the decisions reach the ledger. An expert's description replaces the generated one in the next export, so a second round trip plans zero decisions.

ekos import linkml is human-only, like ekos semantics confirm. No MCP tool can call it, and a test that scans the MCP server's source enforces that.

In the web console

The console's Semantics tab has a LinkML page with two modes: - View shows the schema's classes and enums, filtered by status, with each element's evidence. - Edit is a YAML editor. Validate runs ekos import linkml --dry-run and shows the decisions side by side with the edits. Apply records them as the signed-in reviewer and needs the console's write role.

A real run: LedgerSMB

demo/ledgersmb-linkml/run.sh in the EKOS repository runs the whole path on LedgerSMB, an open-source accounting system in PostgreSQL, PL/pgSQL and Perl. No model is called.

  • Recovered before review: 47 concepts, 222 coded values in 61 columns (179 explained), 60 constraints and 15 gaps, all hypotheses.
  • Reviewed by the script: 21 items confirmed (8 concepts, 12 coded values, 1 constraint), plus one rejection and one rename made by editing the exported YAML.
  • Exported: the confirmed schema has 14 classes and 3 enums in about 730 lines. --status all writes about 5,900 lines.
  • Checked with LinkML's own tools: linkml-lint reports 0 errors (83 style warnings: undocumented columns, and codes used as value names). gen-json-schema and gen-pydantic both run. linkml-validate rejects a record with category: X, because the confirmed CHECK made category the AccountCategory enum:
[ERROR] 'X' is not one of ['A', 'E', 'I', 'L', 'Q'] in /category

The demo's reviewer is not an accounting expert. Its confirmations are believable readings of LedgerSMB's own comments, not an expert's verdict.

Limits

  • No quality numbers yet. Measuring accuracy needs a gold set written by a domain expert before they see EKOS's output (ekos semantics gold-template, then ekos semantics eval). No such set exists for any project.
  • Column-vs-value rules only. Meaning defined by comparing two columns or a date with today, such as overdue or unpaid, is not recovered.
  • Schema history is not replayed. Migration scripts are not applied on top of the base schema; when a table is defined twice, the last definition read wins.
  • One direction. EKOS exports LinkML and imports edits to its own export. It does not read an existing LinkML schema as a source.