Skip to content

Define one semantic model per document without a wrapper - #383

Merged
khush-bhatia merged 10 commits into
apache:mainfrom
flyrain:yf/single-model-document-proposal
Sep 16, 2026
Merged

khush-bhatia merged 10 commits into
apache:mainfrom
flyrain:yf/single-model-document-proposal

Conversation

@flyrain

@flyrain flyrain commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Change the core JSON/YAML specification so each standalone document contains exactly one semantic model, with model properties directly at the root. Remove both the semantic_model array and its wrapper. For example:

version: 0.2.0.dev0
name: sales_analytics
datasets:
  - name: orders
    source: sales.public.orders

A model still supports multiple datasets, relationships, and metrics. Bulk exchange uses separate documents; this PR does not introduce a bundle format.

Breaking change to the unreleased, mutable 0.2.0.dev0 schema: wrapped models (arrays or objects) are rejected. Migration moves a single model's properties to the root or splits multiple models into separate documents, preserving model contents and using the current specification version. Root-level dialects and vendors declarations remain removed, as established by #397.

Changes

  • Update the core JSON Schema, Markdown specification, YAML reference, and documentation overview.
  • Flatten the canonical TPC-DS example without changing model contents.
  • Update validator traversal and add focused checks for the new shape and rejection of legacy/malformed input.
  • Preserve $defs/SemanticModel and the ontology map's embedded semantic_model property.

Scope and merge coordination: The Python SDK and converter migration is implemented in PR #396, which also updates converter CI to run on shared schema/example changes. Review the spec and migration separately, but do not land this schema change until #396 is approved and its combined spec/consumer CI is green. Coordinate the two merges so the repository is not left with the new schema and old consumers. This PR alone does not migrate those consumers.

Applicable checklist

  • Specification and reference examples updated in core-spec/.
  • Breaking change and migration documented.
  • Validator behavior and focused regression coverage updated.
  • Ontology embedded model definition preserved.
  • No dependencies added.
  • Dev mailing list discussion (pending).
  • Python SDK and converter adoption: #396, required for coordinated landing.

@flyrain flyrain changed the title [DISCUSS] Propose one semantic model per document Define one semantic model per document without a wrapper Sep 11, 2026
@jbonofre
jbonofre self-requested a review September 12, 2026 03:42
@djwaldo

djwaldo commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

The shape change is fine by me. I.e. a document-boundary change, not a model-semantics one. I have
a bidirectional converter in flight (#364, ThoughtSpot); adopting it is small.

One case worth settling first, because it is where "one model per document" meets real vendor
topologies. Tested both directions rather than assumed.

A chasm trap: sales and inventory both joining a shared product, no fact-to-fact join.

Ossie → vendor is unaffected. ThoughtSpot resolves multi-fact chasm traps in its query engine,
so this stays one vendor model and cross-fact questions keep working. Wrapper or root makes no
difference.

Vendor → Ossie is the problem. A modeller may legitimately have split those into two
ThoughtSpot models. I.e. permissions, natural-language scope, grain separation. Converting back, I
get two Ossie documents, each with its own copy of product, and nothing records that
those are the same dataset or that the two models are related. The chasm trap is gone.

This PR does not create that, but it makes it permanent: one model per document and no bundle
means there is nowhere to say "these documents are one estate" or "this dataset is that
dataset."

Which is intended?

  1. Documents are standalone, duplication is expected, dataset identity across documents is out
    of scope. This is fine, but worth stating so converter authors stop trying to preserve it.
  2. It belongs to the deferred bundle work. I.e. in which case cross-document dataset identity is a
    requirement for that design, not just packaging.

Either is defensible. It decides whether a converter should silently duplicate the shared
dataset or report a declared loss, and right now I would be guessing.

@flyrain

flyrain commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

cc @jbonofre @khush-bhatia

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The breaking schema change conflicts with existing consumers, while SDK and converter migration remains deferred.

Pull request overview

This PR changes standalone documents to contain one root-level semantic model, updating the schema, validator, documentation, examples, and tests.

Changes:

  • Removes the standalone semantic_model wrapper.
  • Updates validation and regression coverage.
  • Flattens specifications and the TPC-DS example while preserving embedded models.

A critical compatibility concern remains because existing SDK, converter, and consumer flows still expect the wrapped format.

File summaries
File Description
validation/validate.py Traverses and validates root-level models.
validation/tests/test_validate.py Tests new document shapes and traversal.
validation/test_validate.py Adds CLI regression coverage.
examples/tpcds_semantic_model.yaml Flattens the canonical example.
docs/index.md Updates the overview and migration guidance.
core-spec/spec.yaml Updates the YAML reference structure.
core-spec/spec.md Documents the schema and migration guidance.
core-spec/ossie-schema.json Defines the flat standalone document shape.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread core-spec/ossie-schema.json
@khush-bhatia
khush-bhatia self-requested a review September 14, 2026 18:27
@khush-bhatia

Copy link
Copy Markdown
Contributor

@flyrain Could you fix this please ?
In https://github.com/apache/ossie/blob/main/converters/README.md, it says "Parse the Ossie model: Load the YAML file and iterate over the top-level semantic_model entries."

Comment thread validation/validate.py
Comment on lines +349 to +351
# Semantic checks rely on valid structure; let schema validation report
# malformed inputs (including legacy arrays) without traversing them.
if not errors and isinstance(data, dict) and "datasets" in data:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you create an issue to fix this in a follow up ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard prevents semantic checks from crashing on structurally invalid input. We already have #270 tracking that issue and #271 addressing it. Does that cover the follow-up you had in mind, or were you referring to something else in this condition?

khush-bhatia
khush-bhatia previously approved these changes Sep 14, 2026
@flyrain

flyrain commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

This PR does not create that, but it makes it permanent: one model per document and no bundle means there is nowhere to say "these documents are one estate" or "this dataset is that dataset."

@djwaldo, thanks for raising this. The existing array groups models in one file, but it doesn’t define shared dataset identity across those models. Two copies of product aren’t currently declared to be the same dataset just because they appear in the same document. This PR changes how the models are packaged while leaving that limitation unchanged.

Which is intended?

  1. Documents are standalone, duplication is expected, dataset identity across documents is out
    of scope. This is fine, but worth stating so converter authors stop trying to preserve it.
  2. It belongs to the deferred bundle work. I.e. in which case cross-document dataset identity is a
    requirement for that design, not just packaging.

I see your two options as compatible: cross-model dataset identity is outside the current core spec, and we can add support for it separately. One model per doc still leaves room for shared identifiers, references, or a manifest in the future. If we introduce a shared identity would need an explicit design beyond simply grouping files.

@flyrain

flyrain commented Sep 14, 2026

Copy link
Copy Markdown
Contributor Author

@khush-bhatia thanks a lot for the quick review. Resolved all comments. Pls take another look.

@khush-bhatia

Copy link
Copy Markdown
Contributor

@flyrain Could you please rebase to the latest and resolve conflicts.. We did remove dialects and vendors array from root for good.
Relevant PR
#397

Comment thread core-spec/spec.md Outdated
Comment on lines +102 to +103
| `dialects` | array | No | Expression dialects used in the document |
| `vendors` | array | No | Vendors with custom extensions in the document |

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vendors and dialects should be removed.

Comment thread core-spec/spec.md Outdated
Comment on lines +86 to +89
Each JSON or YAML document represents exactly one semantic model. Model properties
are defined directly at the document root alongside `version`, `dialects`, and
`vendors`; there is no `semantic_model` wrapper. A model can contain multiple
datasets, relationships, and metrics.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove dialects, and vendors from here.

Comment thread core-spec/spec.md
Comment on lines +157 to +160
The reusable `$defs/SemanticModel` schema still describes model contents without
standalone document metadata. In particular, an ontology map continues to embed
those contents under its `semantic_model` property. This standalone document
change does not rename or flatten that ontology property.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead we should update the ontology spec to point to the root of this spec(perhaps "https://github.com/apache/ossie/core-spec/ossie-schema.json") ?

Can be done in a follow up PR though.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, let’s handle that in a follow-up PR. Referencing the root schema would also require version on each embedded semantic model, so we should update the ontology examples, converters, and tests together. I’ll leave the current reference in place for this PR.

@flyrain
flyrain force-pushed the yf/single-model-document-proposal branch from 4826881 to 193c707 Compare September 15, 2026 01:33
@flyrain

flyrain commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

@khush-bhatia , fixed as suggested. PTAL.

@djwaldo

djwaldo commented Sep 15, 2026 •

Copy link
Copy Markdown
Contributor

Thanks for clarification.

khush-bhatia
khush-bhatia previously approved these changes Sep 15, 2026
@kayemkim

Copy link
Copy Markdown
Contributor

Merging this branch (6470653) with current main (fc6c9df): #375 landed yesterday, after the last push here, and its validate_relationship_column_arity still iterates data.get("semantic_model", []). For a flat document that loop is empty, so the arity check never runs. A relationship with from_columns: [customer_id, region_id] and to_columns: [id] prints Validation PASSED on the merged tree, where the wrapper form fails on main with [Arity]. The two test_arity_rejects_mismatched_length_columns cases fail the same way, so Validation CI would go red on main after merge. There is no textual conflict and the PR's runs predate #375, which is why neither GitHub nor CI shows it.

Same fix this PR applies to the other three validators: drop the for model in loop and read relationships from the root. The other 37 pytest cases and the two remaining CI steps pass on the merged tree.

@flyrain

flyrain commented Sep 16, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @kayemkim , fixed it in the new commit. I'd suggest to merge this to avoid further rebase.

@khush-bhatia
khush-bhatia merged commit cc919e9 into apache:main Sep 16, 2026
4 of 10 checks passed
djwaldo added a commit to djwaldo/ossie that referenced this pull request Sep 16, 2026
Follows apache#383, which moved the semantic model's fields to the
document root and removed the `semantic_model` wrapper. `ossie-schema.json`
now requires `version`, `name` and `datasets` at the root and forbids
additional properties, so the converter's emitted documents stopped
validating the moment the branch was rebased.

Emit side is `{"version": ..., **semantic_model}`. The spread cannot clobber
`version`: the model dict only ever holds name/description/datasets/
relationships/metrics plus the `custom_extensions` the stash writes, and no
path on it emits a `version` key.

Read side rejects the old wrapper by name rather than lifting its first
entry, matching the treatment in apache#396. Reading entry zero would convert
silently while discarding any later model, and a document old enough to
carry the wrapper may have moved on elsewhere too; naming the one thing the
reader must change is more useful than a best-effort guess.

`datasets` is `minItems: 1` upstream, so the emit side now reports
TS-MODEL-NO-DATASETS at ERROR when a model yields none. Without it the two
legs disagreed: `to-ossie` exited 0 having written a document that fails the
schema and that this converter's own `to-tml` refuses. Both legs now exit 1
on the same input.

Both fixtures were regenerated by running the migrated converter over their
own TML and asserting the result deep-equals the old expected document with
its wrapper lifted -- so the 1,151 changed fixture lines are a re-rooting,
not a content change.

823 tests pass. The README's mapping row is now pinned by a test, having
been verified by nothing: reverting it left the whole suite green.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@kayemkim kayemkim mentioned this pull request Sep 17, 2026
2 of 17 tasks
baruchoxman added a commit to honeydew-ai/ossie that referenced this pull request Sep 18, 2026
apache#383 moved the Ossie document schema to a single semantic model defined
directly at the document root, and apache#397 dropped root-level `dialects` and
`vendors`. The Honeydew converter still required the `semantic_model: [...]`
wrapper, so it could not read any current Ossie document — including the
TPC-DS example, which its own round-trip test loads.

Read the model from the document root, emit `version` plus the model's
properties with no wrapper, and drop the `vendors` round-trip (the field no
longer exists in the schema, and `additionalProperties: false` now rejects
it). A document that still carries the legacy wrapper gets an error that
says how to migrate it.

The dropped multi-model warning has no meaning now that a document holds
exactly one model.

Part of #418

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
jbonofre added a commit that referenced this pull request Sep 19, 2026
PR #383 moved the Ossie document schema to one semantic model directly
at the document root (dropping the `semantic_model:` wrapper), and #407
fixed the Microsoft converter for it, but the Sigma and NVIDIA GSF
converters were never updated, so their CI failed on main as soon as an
unrelated change touched their paths and re-triggered it.

The Sigma, dbt, and wisdom converters share the `ossie` Python package's
OssieDocument/OssieSemanticModel pydantic models, so fixing OssieDocument
to match the flat schema required updating all three converters (and
their tests/snapshots) together to keep them internally consistent.
NVIDIA GSF parses Ossie YAML directly against a dict shape and is fixed
independently.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
@eisber eisber mentioned this pull request Sep 19, 2026
14 of 17 tasks
djwaldo added a commit to djwaldo/ossie that referenced this pull request Sep 21, 2026
Follows apache#383, which moved the semantic model's fields to the
document root and removed the `semantic_model` wrapper. `ossie-schema.json`
now requires `version`, `name` and `datasets` at the root and forbids
additional properties, so the converter's emitted documents stopped
validating the moment the branch was rebased.

Emit side is `{"version": ..., **semantic_model}`. The spread cannot clobber
`version`: the model dict only ever holds name/description/datasets/
relationships/metrics plus the `custom_extensions` the stash writes, and no
path on it emits a `version` key.

Read side rejects the old wrapper by name rather than lifting its first
entry, matching the treatment in apache#396. Reading entry zero would convert
silently while discarding any later model, and a document old enough to
carry the wrapper may have moved on elsewhere too; naming the one thing the
reader must change is more useful than a best-effort guess.

`datasets` is `minItems: 1` upstream, so the emit side now reports
TS-MODEL-NO-DATASETS at ERROR when a model yields none. Without it the two
legs disagreed: `to-ossie` exited 0 having written a document that fails the
schema and that this converter's own `to-tml` refuses. Both legs now exit 1
on the same input.

Both fixtures were regenerated by running the migrated converter over their
own TML and asserting the result deep-equals the old expected document with
its wrapper lifted -- so the 1,151 changed fixture lines are a re-rooting,
not a content change.

823 tests pass. The README's mapping row is now pinned by a test, having
been verified by nothing: reverting it left the whole suite green.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
MikeNitsenko added a commit to MikeNitsenko/ossie that referenced this pull request Sep 25, 2026
apache#383 moved the one semantic model a document holds to its root and
dropped the `semantic_model` wrapper. The converter still wrote the
wrapper on import and required it on export, so every imported model
failed the spec validator and every current document was refused on
export -- which is what the generated round-trip tests tripped on in CI.

Both directions now use the root mapping as the model. The "several
models, only the first converted" issue goes with the wrapper, since a
document can no longer hold more than one. Fixtures, inline test YAML
and the hand-authored model generator are moved to the flat format.
zackm-solid added a commit to zackm-solid/ossie that referenced this pull request Sep 28, 2026
…paths

Follow main's move to flat single-model documents (apache#383, apache#396):
- Import writes the model's properties at the document root beside
  `version` instead of inside a `semantic_model` list.
- Export reads the model from the root. The legacy wrapped layout
  (Apache Ossie 0.1.1, early 0.2.0.dev0 snapshots) is still read, with a
  warning; an empty or non-list wrapper raises, and extra models warn.
- Import's "did you mean `ossie-solid export`?" hint now recognizes a
  flat Ossie document as well as a wrapped one.
- Fixtures flattened; tpcds_ossie.yaml regenerated, which also drops its
  duplicated `version:` line. The cross-vendor sweep detects flat
  documents again (10 fixtures across 5 converters) instead of skipping.

Read OSSIE_SQL_2026 (apache#439) as a portable, ANSI-compatible dialect:
- Expression fallback is the requested dialect, then ANSI_SQL, then
  OSSIE_SQL_2026; the fallback warning names the dialect actually read.
- OSSIE_SQL_2026 is neither resolved to nor reported as unreadable, and
  maps to sqlglot's default tokenizer.

Trigger the Solid CI on core-spec/**, examples/** and python/** changes,
since the suite validates against the schema and the shared examples.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
MonkeyCanCode pushed a commit that referenced this pull request Oct 1, 2026
…#417)

PR #383 moved the Ossie document schema to one semantic model directly
at the document root (dropping the `semantic_model:` wrapper), and #407
fixed the Microsoft converter for it, but the Sigma and NVIDIA GSF
converters were never updated, so their CI failed on main as soon as an
unrelated change touched their paths and re-triggered it.

The Sigma, dbt, and wisdom converters share the `ossie` Python package's
OssieDocument/OssieSemanticModel pydantic models, so fixing OssieDocument
to match the flat schema required updating all three converters (and
their tests/snapshots) together to keep them internally consistent.
NVIDIA GSF parses Ossie YAML directly against a dict shape and is fixed
independently.

Co-authored-by: Claude Sonnet 5 <[email protected]>
zackm-solid added a commit to zackm-solid/ossie that referenced this pull request Oct 14, 2026
Follow main's move to flat single-model documents (apache#383, apache#396):
- Import writes the model's properties at the document root beside
  `version` instead of inside a `semantic_model` list.
- Export reads the model from the root. The legacy wrapped layout
  (Apache Ossie 0.1.1, early 0.2.0.dev0 snapshots) is still read, with a
  warning; an empty or non-list wrapper raises, and extra models warn.
- Import's "did you mean `ossie-solid export`?" hint now recognizes a
  flat Ossie document as well as a wrapped one.
- Fixtures flattened; tpcds_ossie.yaml regenerated, which also drops its
  duplicated `version:` line. The cross-vendor sweep detects flat
  documents again (10 fixtures across 5 converters) instead of skipping.

Read OSSIE_SQL_2026 (apache#439) as a portable, ANSI-compatible dialect:
- Expression fallback is the requested dialect, then ANSI_SQL, then
  OSSIE_SQL_2026; the fallback warning names the dialect actually read.
- OSSIE_SQL_2026 is neither resolved to nor reported as unreadable, and
  maps to sqlglot's default tokenizer.

Trigger the Solid CI on core-spec/**, examples/** and python/** changes,
since the suite validates against the schema and the shared examples.
Align the workflow with the normalized converter workflows (apache#480):
read-only permissions, fail-fast off, and uv from the pinned setup-uv
action. List converters/solid among the uv projects in dependabot.yml
(apache#497).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
ralfbecher added a commit to ralforion/orionbelt-semantic-layer that referenced this pull request Oct 18, 2026
* fix(osi): export the current Apache Ossie document shape

Apache Ossie replaced the semantic_model array with one model at the
document root (apache/ossie#383) without changing 0.2.0.dev0, so every
exported document failed the current schema and validate.py.

- Export puts the model's properties at the root.
- The vendored schema is the current upstream one: flat root, the
  HOLISTICS_AQL dialect (read as non-SQL), non-empty names, sources and
  keys.
- The validator checks duplicate names and dataset references in flat
  documents, which it skipped; a wrapped document is still walked so a
  legacy input reports every problem.
- Import still reads the semantic_model array, converting its first
  model, now with a warning.
- Tests and fixtures move to the flat shape; the TPC-DS examples are the
  current upstream ones, and the TPC-DS OBML sample is regenerated.
- Docs describe the flat shape and drop the claim that the export writes
  root dialects/vendors arrays.

* fix(osi): export nested-only data objects, guard malformed identifiers

A data object with nestedIn and no code exported source "", which the
current schema rejects (minLength 1). Ossie allows a query as the source,
so it now exports the unnest in the base dialect's form, selecting each
column's code from the element; a nested parent becomes a derived table
and a struct path is split per segment. An obml_source_unnest flag lets
the import restore the object without code, database or schema. The
query runs as written on Postgres (verified); other engines spell the
unnest differently, which the docs state.

The validator hashed dataset names and relationship ends, so a
non-string identifier crashed it with TypeError before the schema errors
came back. The semantic checks now consider only non-empty string
identifiers; anything else is already a schema error.

* fix(osi): nested source queries run on Postgres for chains and struct paths

A nested parent's subquery projected only its declared columns, so a
child unnesting from an undeclared field (Charge Labels -> x_Meta.Notes)
referenced a column the subquery dropped. The subquery now also selects
the first path segment of every object nested in it.

A struct path rendered as "C"."Meta"."Labels", which Postgres reads as a
three-part table name. It now uses composite field access,
("C"."Meta")."Labels", as OrionBelt's Postgres dialect does.

Verified on Postgres with a three-level chain ending in a struct path and
a struct path off the root table.

* fix(osi): select each physical field once in a nested source query

Two OBML columns may share a code (Label Key and Label Code over Key).
The nested source projected it twice, so reading either exported field
failed on Postgres with an ambiguous column reference. The projection now
lists each code once; both fields still export.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants