EKOS — Enterprise Knowledge Operating System

Pentaho and SQL recovery, tested cold. Not a demo fixture.

Two real GitHub ETL projects, cloned fresh and run through the full EKOS pipeline with nothing staged in advance — the terminal output, the MCP query/answer transcripts, and the gaps it found are all reproduced here, from a data engineer's point of view.

pih-pentaho· adventureworks datamart ekos build/recover/resolve/compile/commit ekos ask / ekl / mcp serve
§ 01 / setup
What was actually run

Cloned cold. Compiled once. Cross-checked against the raw files.

pih-pentaho is Partners In Health's production OpenMRS → warehouse ETL — a live, actively maintained clinical-data pipeline. The AdventureWorks project is a grad-school ETL exercise. Neither was touched or tuned before running EKOS against it.

  • pih-pentaho60 Kettle files · 27 SQL files · 403 commits · 14 contributors
  • adventureworks10 Kettle files · 5 SQL files · 3 commits · 1 contributor
  • Commandekos init && ekos build && ekos recover && ekos resolve && ekos compile && ekos commit
§ 02 / terminal
Real output, not a mockup

Every line below came from an actual run.

$ ekos doctor
EKOS Doctor
────────────────────────────
[OK]  Rust toolchain
[OK]  Working directory
[OK]  ekos.toml
[OK]  .ekos/
[OK]  LLM provider  claude (key: $ANTHROPIC_API_KEY ✓)
────────────────────────────
All checks passed.
$ ekos recover — pih-pentaho
Recover complete.
  SQL files analysed: 27
  Git commits analysed: 403
  Pentaho jobs analysed: 60
  IR nodes (Pentaho): 312 total, 33% mapped
  IR nodes (SQL): 18 total, 100% mapped
  Passes run: 57
§ 03 / evidence
What "evidence-backed" means, concretely

Every extracted fact cites the exact XML it came from.

transforms/load-fact-coded-values.ktr
Object :0 (TransformNode)
  node_type: "Source"
  object_name: "omrs_obs"

Object :1 (TransformNode)
  node_type: "Sink"
  object_name: "fact_patient_coded_value"
  columns: [patient_id, gender_id, age_id,
    date_id, ... coded_value_id]

The real Kettle steps, read straight from the file: Import Coded Obs (TableInput) → Write Coded Obs (TableOutput).

  • MatchExact — source table, sink table, and every dimension foreign key column, all recovered without an LLM.
  • Why it mattersStructural parsing (roxmltree), not generation — nothing here was guessed.
§ 04 / graph
Beyond one file

A compiled graph connects facts a grep never would.

Asking about load-fact-coded-values.ktr surfaced its most tightly co-changed files across 403 commits of git history — without anyone documenting that relationship anywhere.

  • Co-changeload-fact-numeric-values.ktr, load-fact-patient-event.ktr, reference-data setup transforms
  • IdentityMerged Todd Anderson and toddrobertanderson as the same person — recovered from git author metadata alone
375
objects committed
1,314
relationships
791
evidence records
12
identity merges, 0 conflicts
§ 05 / limits
Shown, not hidden

The AdventureWorks project exposed two real gaps.

Signalpih-pentahoadventureworks
Pentaho step mapping33%33%
SQL DDL mapping100% (18/18)0% (0/5)

Every AdventureWorks SQL file failed to parse — MySQL # comments and multi-statement scripts the generic SQL dialect can't handle. Zero tables were fabricated to compensate; the pipeline logged the failure and moved on with an empty graph for those files. A bonus finding: the localdocs observer also caught and stripped an invisible, instruction-shaped payload hidden inside a course PDF in that same repo, before it ever reached an LLM-backed pass.

§ 06 / mcp
A DE's AI agent, talking to the ledger

ekos mcp serve — query in, evidenced answer out.

live JSON-RPC over stdio
→ tools/call ekos_search { "query": "omrs_obs" }

← result {
  "matches": [
    { "id": "889b30d6-...", "name": "omrs_obs" },
    { "id": "01e3d482-...", "name": "omrs_obs_group" },
    { "id": "f7238521-...", "name": "import-into-omrs-obs.ktr:0" },
    ... 34 more, ranked by relevance
  ]
}

No LLM generation happened here — FTS-ranked retrieval, straight from the ledger, before the agent writes a single sentence.

§ 07 / mcp
The DE question, live: "what would this ripple into?"

Multi-hop impact trace through a real transformation.

ekos_impact — 5 hops, dependents of step :0
→ tools/call ekos_impact { "id": "f7238521-...", "direction": "dependents", "max_hops": 5 }

← result { "count": 5, "hops": [
  { "hop": 1, "name": "...ktr:4", "via": "FeedsInto" },
  { "hop": 2, "name": "...ktr:6", "via": "FeedsInto" },
  { "hop": 3, "name": "...ktr:8", "via": "FeedsInto" },
  { "hop": 4, "name": "...ktr:7", "via": "FeedsInto" },
  { "hop": 5, "name": "...ktr:3", "via": "FeedsInto" }
]}

The exact question a DE asks before editing an early step in a 12-step pipeline — answered without opening the file.

§ 08 / gap
Same session, an honest gap

Impact analysis doesn't yet cross the SQL ↔ Pentaho boundary.

ekos_dependents — fact_patient_coded_value (Table)
→ tools/call ekos_dependents
   { "id": "a77168f6-..." }

← result {
  "target": { "kind": "Table" },
  "dependents_count": 0,
  "dependencies_count": 0
}

But §04 already showed load-fact-coded-values.ktr writes exactly this table. The SQL Table object and the Pentaho Sink node referencing it by name aren't yet resolved to the same identity with a graph edge between them.

Worth knowing before relying on cross-format impact queries for SQL-defined tables touched only by Pentaho jobs.

§ 09 / for data engineers
What this is actually worth

Four guarantees a raw-file grep doesn't give you.

Inherit legacy ETL without an owner

Ask the ledger instead of reverse-engineering undocumented Kettle XML by hand.

Impact analysis before you touch anything

ekos_impact/ekos_dependents answer "what breaks?" from the real dependency graph.

Migration plans grounded in real logic

Moving Kettle to dbt/Airflow against an already-extracted Source→Sink shape, not a re-read of 40 XML files.

Know the boundary, don't assume it

SQL-defined tables and Pentaho steps that reference them aren't linked yet — a real limit, not a hidden one.

Try it

Point an agent at the pipeline — not the XML, the compiled logic.

terminal
# compile every .ktr/.kjb and SQL file once
$ ekos build && ekos recover && ekos resolve && ekos compile && ekos commit

# ask questions, or run deterministic queries
$ ekos ask "what does fact_sales depend on?"
$ ekos ekl "FIND Object WHERE kind = 'Table'"

# serve it to any MCP client over stdio
$ ekos mcp serve --workspace .
MCP toolAnswers
ekos_impactWhat breaks if I change this table, job, or step
ekos_dependentsWhat already depends on this object
ekos_diffWhat changed in the ledger since a given time
EKOS · Pentaho + SQL recovery analyzers · github.com/alexeyban/EKOS