mdcode: add OWL export (OSI -> Turtle), the round-trip inverse of owl import - #340
Draft
libei wants to merge 4 commits into
Draft
mdcode: add OWL export (OSI -> Turtle), the round-trip inverse of owl import#340libei wants to merge 4 commits into
libei wants to merge 4 commits into
Conversation
… import `kcmd owl export <model.yaml>` writes a semantic model back out as a Turtle ontology -- the exact mirror of `owl import`. It maps the IR to the staging OwlModel (from_ir.ts) and serializes it to Turtle (serialize.ts), reusing the loader for OSI YAML -> IR. Scope is round-trip fidelity: a model that originated as OWL exports losslessly (OWL -> OSI -> OWL -> OSI is stable at the IR level), re-emitting the carried GOOGLE owl:/rdfs: extensions verbatim and rebuilding shortened in-namespace names from owl:baseIri. Constructs OWL cannot express (metrics, non-column expressions, bound sources, associations, composite unique keys, deployment targets) are dropped with a warning, never misrepresented. Every OWL import fixture round-trips unchanged; sales is pinned to a golden .ttl. Docs updated (owl-import.md gains an Exporting to OWL section).
Address code-review findings on the OSI -> OWL exporter: several hand-authored-model cases silently lost or misrepresented data instead of following the "dropped with a warning, never misrepresented" contract. - Multi-domain field merge: a same-named field defined differently on two entities is now compared (datatypeFactsKey) and warned, keeping the first domain's definition instead of silently taking it. - inverseFunctional across domains: a single-column unique key on only some domains of a shared field now warns instead of being dropped from the non-first domains. - Dimension flag: a field whose dimension role won't survive re-import (non-temporal marked time, temporal marked not, temporal with no flag) now warns, matching the checkFieldRepresentable comment. - Model description: a model with no description now warns that re-import synthesizes a placeholder. - Relationship comment: a relationship with both ai_context.instructions and a description now warns that the description is dropped. - Bare carried cross-reference with no owl:baseIri now expands against the serializer's DEFAULT_BASE, emitting a valid absolute IRI instead of a broken relative <name>. - Remove the unused entityNames parameter from isDestKeyColumn. Add targeted tests for each warning path; extend the export doc's "dropped with a warning" list. All fixtures still round-trip cleanly.
…anches The export suite round-tripped only OWL-origin fixtures and covered the headline loss cases, leaving several exporter branches with no direct test. Add: - An OSI-origin round-trip fixture (directory.osi.golden.yaml) and its two tests, the mirror of the OWL-origin suite. It is a hand-authored, fully-OWL-expressible fixed point whose distinctive job is the cross-entity field-order merge (orderFields): a multi-domain field sits at different absolute positions on its two domains, so the single exported property order must be a linear extension of both. A test pins that each entity's own field order survives re-import. - Tests for the previously-untested export warning branches: an abstract entity, an imported vendor expression, a relationship endpoint outside the model, bound join columns, a non-GOOGLE vendor extension, an unparseable GOOGLE extension, and orderFields' cycle warning (entities that disagree on a shared field's relative order). Pure test additions; no exporter changes. Full semantic suite: 506 pass.
- Rename directory.osi.golden.yaml -> directory.osi.yaml. In fixtures/owl a *.osi.golden.yaml is the derived golden OUTPUT of importing its paired *.owl.ttl; this fixture is a hand-authored SOURCE input with no such pair, so the .golden infix misled. The rename also correctly opts it into the osi-schema.json guardrail (which only enforces schema validity on source *.osi.yaml files), so add it to the same extends/abstract allow-list as the hand-authored hierarchy fixture. - Bind the warning assertions to the specific construct (entity 'Customer' is abstract, relationship 'placedBy' has bound join columns, ACME (vendor extension), GOOGLE (unparseable data)) so a regression that fires the wrong warning can no longer pass on a loose substring match. - Use a non-temporal field in the bound-join-columns test so the only warning it produces is the one under test (a Date field emitted its own incidental time-dimension warning). - Dedupe the two OSI-origin tests' fixture load behind a local helper.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
kcmd owl export <model.yaml>— the reverse ofkcmd owl import. It writes a semantic model back out as a Turtle (.ttl) OWL ontology, the exact mirror of the import pipeline:from_ir.ts— IR → stagingOwlModel(the inverse ofto_ir.ts)serialize.ts—OwlModel→ Turtle (the inverse ofparse.ts)convert.ts— newconvertOsiToOwl()orchestrator;commands.ts/main.tsgain theexportactionScope: round-trip fidelity
This was scoped deliberately (not a general "model → OWL" claim). A model that originated as OWL exports losslessly:
OWL → OSI → OWL → OSIis stable at the IR level. It:owl:Class, field →owl:DatatypeProperty, relationship →owl:ObjectProperty,primary_key→owl:hasKey, single-colunique_key→owl:InverseFunctionalProperty,extends→rdfs:subClassOf, datatype → itsxsd:range);owl:/rdfs:extensions verbatim (owl:inverseOf,rdfs:subPropertyOf, property characteristics, per-term annotations, …), rebuilding a shortened in-namespace name to a full IRI from the carriedowl:baseIri;A model authored natively can hold things OWL has no shape for — a
metric, a field whoseexpressionis not a bare column, animportedExpression, a boundsource, an M:Nassociation, a compositeunique_key, deployment targets. Those are dropped with a warning, never misrepresented; the class/property/edge they hang off is still exported.Tests
owl_export.test.ts: all five OWL import fixtures round-tripOWL → OSI → OWL → OSIunchanged (zero warnings);salesis pinned to a goldensales.owl.golden.ttl; the golden re-imports to the same model; and each non-OWL construct is verified to warn + drop.test:semanticsuite (488 tests) green; verified end-to-end through the real CLI (kcmd owl export→kcmd owl importround-trips).Docs
owl-import.mdgains an Exporting to OWL section and the reverse-direction limitation is updated;README.mdmentions the new command.