ODCS integration and interop¶
The Open Data Contract Standard (ODCS, a Bitol / Linux Foundation project) is the settled industry format for describing data contracts. ODCS is deliberately a document standard — it describes the contract and delegates execution to engines. PyCharter plugs into that ecosystem as an execution engine: the same contract that round-trips through ODCS is what PyCharter enforces in-process — on API payloads, Kafka records, and ETL batches.
Export: to_odcs¶
from pycharter.contract_io import to_odcs
odcs_document = to_odcs(contract_dict) # ODCS v3.1.0, JSON-serialisable
odcs_v32 = to_odcs(contract_dict, api_version="v3.2.0") # opt-in native guidance
The exporter targets ODCS v3.1.0 and its output validates against the published v3.1.0 JSON schema (strict mode — the schema is vendored in the test suite and every export corpus document is checked). Highlights of the mapping:
The optional v3.2.0 export restores imported top-level and schema
context, synonyms, semanticType, authoritative definitions, enum labels,
and unchanged map/vector shapes to native ODCS fields. It retains the original
foreign source under customProperties.pycharter for lossless re-import.
The default remains v3.1.0 for existing consumers. Executable enum and
map/vector changes in the canonical contract take precedence over stale
source guidance. The v3.2 export is checked against the published v3.2 schema.
| PyCharter | ODCS v3.1.0 |
|---|---|
json_schema.properties |
schema[0].properties[] — date / date-time / time formats become first-class logical types; constraints live in logicalTypeOptions; nested objects and array items map natively |
x-database.sql_type / x-database.primary_key |
physicalType / primaryKey per property |
x-database-table.table_name |
schema[0].physicalName |
metadata.governance_rules.pii_fields |
classification: pii per property |
field_bindings primary concepts |
per-property authoritativeDefinitions (type: businessDefinition, URL = the SKOS concept IRI) |
metadata.title / .version / .status / .description |
document name / version / status / description.purpose |
validation_rules.rules (per-record gates) |
per-property quality[] entries, type: custom, engine: pycharter |
data_quality.rules |
per-property or schema-level quality[]; library metrics normalised to metric (rowCount, nullValues, …) |
data_quality.targets |
slaProperties[] with driver: quality |
metadata.ownership |
team.members[] (full metadata also rides in customProperties for lossless re-import) |
coercion_rules, field_bindings, versions |
customProperties[] pairs |
The export is guaranteed lossless: every JSON Schema key without a
native ODCS home is preserved verbatim in customProperties, and a CI
gate round-trips every contract in the repository
(from_odcs(to_odcs(c)) == c) on every build.
Import: native ingestion¶
An ODCS DataContract document is accepted anywhere a contract is
accepted — auto-detected by its kind and apiVersion keys, no
explicit conversion call needed:
from pycharter import Validator
from pycharter.contract_parser import parse_contract_file
validator = Validator.from_dict(odcs_document) # enforce directly
metadata = parse_contract_file("orders.odcs.yaml") # files too
from_odcs remains available for explicit conversion:
Input is tolerant by design: v3.0.x, v3.1.x, and v3.2.x documents are
accepted, with customProperties as the v3.1 pair list or a plain
mapping — so contracts authored by other tools import regardless of
dialect. ODCS sections PyCharter does not model (servers,
support, price, roles, …) are preserved under
metadata.odcs_passthrough, never silently dropped.
Documents with multiple schema objects fail import with a
ConfigValidationError; split them into one object per document before
loading. This also applies when Validator.from_dict or parse_contract
detects ODCS input automatically.
For a foreign v3.2 document, context, schema object guidance, synonyms,
semanticType, and authoritative definitions remain in
metadata.odcs_passthrough, with odcs_import_review: required. They are
source material for a reviewer, not approved agent instructions or runtime
rules. ODCS enum values become executable JSON Schema enum values; map and
vector properties become JSON Schema object and fixed-length array shapes.
The original ODCS schema remains available for details PyCharter cannot
execute, including synonym metadata and vector distance metrics. Non-string
map keys and key constraints fail import rather than pass with an unenforced
rule. Imported guidance is reviewed as one bundle per exact contract version.
The proposal service requires an explicit approval by a different,
authenticated reviewer before guarded publication, even when native proposals
permit commit without approval. The HTTP approval route uses the verified JWT
workspace identity. Local CLI --as labels and anonymous development access do
not satisfy this requirement.
Published metadata records odcs_import_review: guidance_reviewed, the reviewer,
approval time, and odcs_guidance_review evidence reference. Its fingerprint
binds the namespace, name, version, and source bundle. Candidate-supplied approval
claims are discarded when staging. Direct store and API publication remain
permitted; their unreviewed guidance stays withheld from agents.
Reviewer proposal detail shows source material as imported_unapproved until
publication, then as imported_reviewed_advisory when the durable evidence
matches. Full authenticated preparation also returns the reviewed bundle under
import_guidance, explicitly labelled as source data rather than host/system
instructions or executable rules. Metadata claims alone cannot enable it. A
bundle containing a private field is withheld as a unit, and offline,
compact, or budgeted views omit it. MCP schema fetch, HTTP Try it, the agent
CLI, and provider adapters continue omitting imported descriptions from
model-facing schemas after review. Earlier contract_reviewed versions need a
new version and authenticated bundle approval; their trust is not upgraded.
The v3.2 behavior follows the
ODCS schema definition
and context definition.
Round trip (from_odcs(to_odcs(c))) reproduces the canonical contract
exactly — PyCharter-authored documents carry a pycharter
customProperty that anchors format version and authored shape. The
documented normalisations: ODCS library metric values come back as
canonical PyCharter rule names with type: library, data_quality.rules
regroup by column,
and id/tags are regenerated deterministically on each export.
Numeric metric assertions use native ODCS entries when their authored shape
fits the chosen schema. Shapes that ODCS 3.⅓.2 cannot represent exactly use
the existing engine: pycharter custom encoding, including equal range
endpoints, snake case operators, numeric bound strings, multiple operators,
and an omitted assertion. Import restores the authored operators and rule
type. See numeric assertions; this
lossless encoding does not make a custom rule executable by another engine.
PyCharter and datacontract-cli¶
datacontract-cli is the ODCS reference implementation. The two tools own different halves of the contract lifecycle, and they compose:
| Lifecycle stage | Tool |
|---|---|
| Author, lint, and diff contracts; generate code/schemas (SQL, Avro, dbt, …) | datacontract-cli |
| Verify contracts in CI and batch against warehouses, lakes, files | datacontract-cli |
| Enforce contracts in-process at runtime — API payloads, Kafka records (per message), ETL stage boundaries | PyCharter |
| Versioned contract registry + quality scoring + governed agent context | PyCharter |
A combined workflow: author and CI-gate the ODCS document with
datacontract-cli, from_odcs it into PyCharter for runtime
enforcement inside the producing service, and to_odcs any
PyCharter-authored contracts back out so the rest of the ODCS toolchain
(catalogs, datacontract-cli itself) can consume them. PyCharter is not
a rival standard — it exports the standard.
Where Soda, Great Expectations, and dbt attach¶
Soda and Great Expectations execute quality checks warehouse-side
(scheduled scans, post-load); dbt tests run inside dbt after
materialisation. ODCS models all of them as pluggable quality engines,
exactly as it models PyCharter (engine: pycharter on custom quality
entries). The practical split: PyCharter rejects or coerces bad
records before they land; warehouse-side engines measure what did
land. Many stacks run both.
See also: PyCharter vs. the alternatives for the full capability matrix, and Agent integration (MCP) for serving contract-bound context to AI agents.