Core spec: allow ai_context and custom_extensions on the document root - #323
Core spec: allow ai_context and custom_extensions on the document root#323eisber wants to merge 2 commits into
Conversation
The root object is the only node in the spec with no ai_context and no custom_extensions: both appear on SemanticModel, Dataset, Field, Metric and Relationship, but the document itself is closed (additionalProperties: false) with only version and semantic_model. A document holds a LIST of semantic models, so guidance that governs all of them has nowhere to live. A producer emitting one model per data source must copy shared instructions into every model so no model can be read without them, and a consumer cannot tell those copies apart from genuinely model-specific instruction. Document-wide context is already an established shape in Ossie: an ontology document carries ai_context on its root and $refs this specification's AIContext definition. Core documents were the only root without it, so this follows the existing precedent rather than introducing a new concept, and a test pins the two roots to the same definition. Both keys are optional and the root stays closed, so existing documents validate unchanged. Refs: apache#322
There was a problem hiding this comment.
Pull request overview
Adds support for document-scoped guidance and vendor metadata by allowing ai_context and custom_extensions on the core spec document root, aligning the core document root with patterns already used elsewhere (and with the ontology root).
Changes:
- Extend
core-spec/osi-schema.jsondocument root with optionalai_contextandcustom_extensions(root remains closed). - Update the Python reference model and tests to parse/serialize
ai_context/custom_extensionsat the document level. - Update spec/docs to document the new document-level fields and their precedence semantics.
Show a summary per file
| File | Description |
|---|---|
| python/tests/test_models.py | Adds regression tests asserting schema/root parity and round-trip serialization for document-level ai_context and custom_extensions. |
| python/src/ossie/models.py | Adds ai_context and custom_extensions to the OSIDocument Pydantic model. |
| docs/index.md | Updates docs text to include document-level AI context. |
| core-spec/spec.yaml | Documents root-level ai_context / custom_extensions in the YAML spec summary. |
| core-spec/spec.md | Adds a new “Document” section with a worked example and precedence semantics. |
| core-spec/osi-schema.json | Adds the new optional root properties with descriptions; keeps additionalProperties: false. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 6/6 changed files
- Comments generated: 2
- Review effort level: Lite
| version: str = "0.2.0.dev0" | ||
| dialects: Optional[list[OSIDialect]] = None | ||
| vendors: Optional[list[OSIVendor]] = None | ||
| ai_context: Optional[OSIAIContext] = None | ||
| semantic_model: list[OSISemanticModel] | ||
| custom_extensions: Optional[list[OSICustomExtension]] = None |
There was a problem hiding this comment.
Confirmed — the diagnosis is right. Reproduced against this branch:
emitted root keys: ['version', 'dialects', 'vendors', 'semantic_model']
schema errors: ["Additional properties are not allowed ('dialects', 'vendors' were unexpected)"]
Two reasons I'm not making that change here, though:
-
It's pre-existing, not introduced by this PR.
dialects/vendorsare already onOSIDocumentinmain; this PR only addsai_contextandcustom_extensionsalongside them. -
A removal limited to
models.pywould break converters. Rootvendorsis still emitted by the Honeydew converter (converters/honeydew/src/honeydew_osi/converter.py:605-611) and asserted by its tests (test_vendors_roundtrip). Dropping the model fields without the converter side would just relocate the failure.
That work is already in flight in two open PRs — #148 (dbt side + OSIDocument) and #306 (dbt + Honeydew + rejecting root extras). #306 in particular covers the converter half. Issue #322 calls this out explicitly: this PR "goes the other way round — it adds to the schema first and keeps the root closed", deliberately compatible with #148/#306.
So I'd rather let those land the removal than half-do it here and conflict with both. Nothing in this PR makes the situation worse: the root stays additionalProperties: false, and the two optional keys it adds are the only additions.
Side note for whoever picks up #148/#306: OSIVendor has no HONEYDEW member, so OSIDocument.model_validate() currently rejects the Honeydew converter's own output. Schema-side $defs/Vendor is free-form, so custom_extensions is unaffected — it's purely the root vendors field, which those PRs remove anyway.
| # Optional: Document-wide context for AI tools. | ||
| # Applies to every semantic model in this document. Model-level ai_context adds to | ||
| # it and takes precedence where the two conflict. | ||
| # Use this for guidance that governs the document as a whole — a model read in | ||
| # isolation would otherwise have to repeat it. | ||
| # Can be a simple string or a structured object with instructions, | ||
| # synonyms, examples, or vendor-specific keys. | ||
| ai_context: | ||
| instructions: string | ||
| synonyms: | ||
| - string | ||
| examples: | ||
| - string |
There was a problem hiding this comment.
Good catch on the inconsistency — but I've fixed it in the opposite direction, because the string form is the side that's already been agreed to be wrong.
That contradiction is tracked in #141: spec.yaml says ai_context: string at every level, while osi-schema.json ($defs/AIContext) and the canonical examples/tpcds_semantic_model.yaml both use the structured object. The resolution direction there is to tighten spec.yaml up to the schema, not to push the schema's structured form down to string. Declaring the root as string here would move away from that and remove the only structured example in the file.
While checking this I did find a real problem with the block — one that's only visible with more of the file in view. The new keys had landed inside the # Enumerations section (after the --- on line 27, among dialects/datatypes/vendor_name, which are enum definitions, not root keys). Parsed, that section read:
['dialects', 'datatypes', 'vendor_name', 'ai_context', 'custom_extensions', 'semantic_model']
Fixed by giving the root keys their own --- section with a # Document root header, matching how every other section in this file is delimited. It now parses as:
0 -> ['version']
1 -> ['dialects', 'datatypes', 'vendor_name']
2 -> ['ai_context', 'custom_extensions', 'semantic_model']
3 -> ['datasets'] ...
which also pulls semantic_model out of the enum section, where it had been sitting on main.
I did take your "point readers to the authoritative definition" suggestion: the comment now states that the structured form is the one shown and refers to AIContext in osi-schema.json as authoritative for both forms. Aligning the four remaining model-level ai_context: string sites is #141's scope, so I've left those untouched here.
The new root-level ai_context and custom_extensions had been added inside
spec.yaml's `# Enumerations` section, so they parsed as part of the enum
block alongside dialects, datatypes and vendor_name:
['dialects', 'datatypes', 'vendor_name', 'ai_context',
'custom_extensions', 'semantic_model']
Give the document-root keys their own `---` section with a `# Document root`
header, matching how every other section in the file is delimited. This also
pulls semantic_model out of the enum block, where it had been sitting before.
Also note in the comment that the structured form is the one shown and that
the AIContext definition in osi-schema.json is authoritative for both the
string and object forms. Aligning the remaining model-level `ai_context:
string` sites is tracked separately in apache#141.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
The document root is the only node in the core spec with no
ai_contextand nocustom_extensions. Both keys appear onSemanticModel,Dataset,Field,MetricandRelationship; the root is closed (additionalProperties: false) with onlyversionandsemantic_model.Since
semantic_modelis a list, guidance that governs every model in a document has nowhere to live. A producer emitting one model per data source must copy shared instructions into each model, and a consumer cannot tell those copies apart from genuinely model-specific instruction. See #322 for the full motivation.This PR adds two optional keys to the document root:
ai_context$ref: #/$defs/AIContextcustom_extensions#/$defs/CustomExtensionSemantics: document-level context applies to every semantic model in the document; model-level
ai_contextadds to it and takes precedence where the two conflict.Precedent
This is not a new concept for the project. An ontology document already carries
ai_contexton its root, and$refs this spec's definition:The core spec already defines document-wide AI context — the core document root was simply the only root that never consumed it. A test pins both roots to the same
AIContextdefinition so they cannot drift.Not a breaking change
$defs— both keys reuse existing definitions.requiredis unchanged; both keys are optional.additionalProperties: falseis unchanged, so the root stays closed. Unknown root keys, a wrong-typedai_context, and an extension missingdataare all still rejected.examples/tpcds_semantic_model.yamlvalidates unchanged, and absent keys do not serialize.Interaction with other open work
dialects,vendors) that were never in the schema. This PR is compatible in substance and goes the other way round: it adds to the schema first and leaves the root closed. Under Keep converter outputs aligned with root schema #306'sextra="forbid", declared fields still validate.spec.yamlto showai_contextas a structured object (drift core-spec/spec.yaml declaresai_context: stringbut the JSON schema + canonical example use a structured object #141). The root entry added here uses docs: align spec yaml ai_context shape #221's shape, so it does not re-introduce the drift.ai_contextshould exist at all. That is a broader question; this PR only makes the existing construct available at the one level where it is missing.Related Issues
Refs #322
Checklist
Specification
core-spec/and follow the existing structureOntology
ontology/are consistent with spec changes —ontology/is untouched; it already had rootai_contextand this aligns core with itConverters
converters/is updated to reflect spec or ontology changes — not applicable, both keys are optional and no converter emits themValidation
validation/are updated if the spec changed —validate.pyis schema-driven and needed no change; verified positive, negative and back-compat cases against itDocumentation
docs/is updated to reflect any user-facing changesspec.mdwith a worked exampleCONTRIBUTING.mdis updated if the contribution process changed — not applicableExamples
examples/are added or updated for any new spec constructs — no example changes; the worked example lives inspec.md, andtpcds_semantic_model.yamlis intentionally left unchanged to demonstrate back-compatTests
pytest/ CI green) — 9 -> 14 passingAIContextdefinitionCompliance