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 allwrites about 5,900 lines. - Checked with LinkML's own tools:
linkml-lintreports 0 errors (83 style warnings: undocumented columns, and codes used as value names).gen-json-schemaandgen-pydanticboth run.linkml-validaterejects a record withcategory: X, because the confirmedCHECKmadecategorytheAccountCategoryenum:
[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, thenekos 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.