agentic-data-contracts 0.62.0__tar.gz → 0.63.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/.github/workflows/ci.yml +8 -8
  2. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/.gitignore +8 -1
  3. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/.pre-commit-config.yaml +2 -2
  4. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/CHANGELOG.md +31 -0
  5. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/PKG-INFO +48 -36
  6. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/README.md +47 -35
  7. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/architecture.md +10 -4
  8. agentic_data_contracts-0.63.0/docs/paper/ACM-Reference-Format.bst +2950 -0
  9. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/Makefile +4 -4
  10. agentic_data_contracts-0.63.0/docs/paper/abstract.txt +1 -0
  11. agentic_data_contracts-0.63.0/docs/paper/acmart.cls +3614 -0
  12. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/main.tex +19 -10
  13. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/preamble.tex +20 -0
  14. agentic_data_contracts-0.63.0/docs/paper/pvldb.sty +87 -0
  15. agentic_data_contracts-0.63.0/docs/paper/pvldb.tex +65 -0
  16. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/refs.bib +315 -22
  17. agentic_data_contracts-0.63.0/docs/paper/sections/00-abstract.tex +19 -0
  18. agentic_data_contracts-0.63.0/docs/paper/sections/01-intro.tex +133 -0
  19. agentic_data_contracts-0.63.0/docs/paper/sections/02-background.tex +133 -0
  20. agentic_data_contracts-0.63.0/docs/paper/sections/03-system.tex +147 -0
  21. agentic_data_contracts-0.63.0/docs/paper/sections/04-design.tex +320 -0
  22. agentic_data_contracts-0.63.0/docs/paper/sections/05-knowledge.tex +223 -0
  23. agentic_data_contracts-0.63.0/docs/paper/sections/06-delivery.tex +128 -0
  24. agentic_data_contracts-0.63.0/docs/paper/sections/07-authoring.tex +158 -0
  25. agentic_data_contracts-0.63.0/docs/paper/sections/08-buys.tex +132 -0
  26. agentic_data_contracts-0.63.0/docs/paper/sections/09-threats.tex +139 -0
  27. agentic_data_contracts-0.63.0/docs/paper/sections/10-conclusion.tex +58 -0
  28. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/A-golds.tex +20 -1
  29. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/C-leniency.tex +22 -14
  30. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/D-harness.tex +3 -2
  31. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/G-leaderboard.tex +1 -1
  32. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/H-escalations.tex +2 -4
  33. agentic_data_contracts-0.63.0/docs/paper/sections/I-predictions.tex +119 -0
  34. agentic_data_contracts-0.63.0/docs/paper/sections/J-single-runs.tex +628 -0
  35. agentic_data_contracts-0.63.0/docs/paper/sections/K-cross-family.tex +66 -0
  36. agentic_data_contracts-0.63.0/docs/paper/sections/L-dataagentbench.tex +69 -0
  37. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper-plan.md +19 -0
  38. agentic_data_contracts-0.63.0/docs/superpowers/plans/2026-10-06-livesqlbench-compiler.md +3598 -0
  39. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/superpowers/specs/2026-10-06-livesqlbench-harness-design.md +28 -3
  40. agentic_data_contracts-0.63.0/docs/superpowers/specs/2026-10-19-experiment-4b-design.md +399 -0
  41. agentic_data_contracts-0.63.0/docs/superpowers/specs/2026-10-20-experiment-6-disclosure-scale-design.md +424 -0
  42. agentic_data_contracts-0.63.0/docs/superpowers/specs/2026-10-21-paper-v2-design.md +537 -0
  43. agentic_data_contracts-0.63.0/docs/superpowers/specs/2026-10-23-paper-v3-restructure-design.md +573 -0
  44. agentic_data_contracts-0.63.0/docs/superpowers/specs/2026-11-01-experiment-7-dataagentbench-design.md +418 -0
  45. agentic_data_contracts-0.63.0/docs/superpowers/specs/2026-11-03-experiment-5e-facts-in-the-contract-design.md +224 -0
  46. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/pyproject.toml +1 -1
  47. agentic_data_contracts-0.63.0/src/agentic_data_contracts/semantic/ossie.py +968 -0
  48. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/checkers.py +30 -3
  49. agentic_data_contracts-0.63.0/tests/fixtures/ossie_flat_nvidia_auto_ontology.yml +94 -0
  50. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_malformed_all_sources.py +2 -2
  51. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_ossie.py +5 -4
  52. agentic_data_contracts-0.63.0/tests/test_semantic/test_ossie_document_shape.py +620 -0
  53. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_checkers.py +67 -3
  54. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/uv.lock +41 -151
  55. agentic_data_contracts-0.62.0/docs/paper/abstract.txt +0 -1
  56. agentic_data_contracts-0.62.0/docs/paper/pvldb.tex +0 -45
  57. agentic_data_contracts-0.62.0/docs/paper/sections/00-abstract.tex +0 -28
  58. agentic_data_contracts-0.62.0/docs/paper/sections/01-intro.tex +0 -126
  59. agentic_data_contracts-0.62.0/docs/paper/sections/02-background.tex +0 -170
  60. agentic_data_contracts-0.62.0/docs/paper/sections/03-system.tex +0 -138
  61. agentic_data_contracts-0.62.0/docs/paper/sections/04-design.tex +0 -184
  62. agentic_data_contracts-0.62.0/docs/paper/sections/05-results.tex +0 -294
  63. agentic_data_contracts-0.62.0/docs/paper/sections/06-why.tex +0 -234
  64. agentic_data_contracts-0.62.0/docs/paper/sections/07-governance.tex +0 -82
  65. agentic_data_contracts-0.62.0/docs/paper/sections/08-threats.tex +0 -87
  66. agentic_data_contracts-0.62.0/docs/paper/sections/09-conclusion.tex +0 -48
  67. agentic_data_contracts-0.62.0/src/agentic_data_contracts/semantic/ossie.py +0 -594
  68. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/.github/dependabot.yml +0 -0
  69. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/.python-version +0 -0
  70. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/CLAUDE.md +0 -0
  71. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/LICENSE +0 -0
  72. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/SECURITY.md +0 -0
  73. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/README.md +0 -0
  74. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/figures/fig-interaction.pdf +0 -0
  75. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/figures/fig-ladder.pdf +0 -0
  76. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/figures/make_figures.py +0 -0
  77. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/B-truncation.tex +0 -0
  78. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/E-domain.tex +0 -0
  79. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/paper/sections/F-interaction.tex +0 -0
  80. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/superpowers/plans/2026-10-06-benchmark-refactor.md +0 -0
  81. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/docs/superpowers/plans/2026-10-06-livesqlbench-benchmark.md +0 -0
  82. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/growth_agent/agent.py +0 -0
  83. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/growth_agent/contract.yml +0 -0
  84. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/growth_agent/expected_output.txt +0 -0
  85. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/growth_agent/semantic.yml +0 -0
  86. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/growth_agent/setup_db.py +0 -0
  87. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/ops_agent/agent.py +0 -0
  88. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/ops_agent/contract.yml +0 -0
  89. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/ops_agent/expected_output.txt +0 -0
  90. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/ops_agent/semantic.yml +0 -0
  91. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/ops_agent/setup_db.py +0 -0
  92. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/agent.py +0 -0
  93. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/check_drift.py +0 -0
  94. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/check_sensitivity.py +0 -0
  95. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/contract.yml +0 -0
  96. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/evaluate_conformance.py +0 -0
  97. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/expected_drift_output.txt +0 -0
  98. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/expected_output.txt +0 -0
  99. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/expected_sensitivity_output.txt +0 -0
  100. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/semantic.yml +0 -0
  101. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/setup_db.py +0 -0
  102. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/verified_examples.yml +0 -0
  103. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/examples/revenue_agent/verify_examples.py +0 -0
  104. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/scripts/check_floor_wheels.py +0 -0
  105. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/scripts/regen_examples.sh +0 -0
  106. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/scripts/trim_dbt_manifest.py +0 -0
  107. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/__init__.py +0 -0
  108. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/adapters/__init__.py +0 -0
  109. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/adapters/_normalizer.py +0 -0
  110. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/adapters/base.py +0 -0
  111. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/adapters/duckdb.py +0 -0
  112. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/ard.py +0 -0
  113. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/bridge/__init__.py +0 -0
  114. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/bridge/compiler.py +0 -0
  115. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/__init__.py +0 -0
  116. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/contract.py +0 -0
  117. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/principal.py +0 -0
  118. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/prompt.py +0 -0
  119. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/recorder.py +0 -0
  120. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/schema.py +0 -0
  121. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/session.py +0 -0
  122. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/core/staleness.py +0 -0
  123. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/py.typed +0 -0
  124. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/semantic/__init__.py +0 -0
  125. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/semantic/_assembly.py +0 -0
  126. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/semantic/base.py +0 -0
  127. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/semantic/cube.py +0 -0
  128. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/semantic/dbt.py +0 -0
  129. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/semantic/yaml_source.py +0 -0
  130. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/tools/__init__.py +0 -0
  131. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/tools/factory.py +0 -0
  132. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/tools/langchain.py +0 -0
  133. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/tools/middleware.py +0 -0
  134. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/tools/pydantic_ai.py +0 -0
  135. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/tools/sdk.py +0 -0
  136. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/__init__.py +0 -0
  137. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/_rows.py +0 -0
  138. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/_scalar.py +0 -0
  139. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/_timewindow.py +0 -0
  140. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/_tolerance.py +0 -0
  141. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/attribution.py +0 -0
  142. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/conformance.py +0 -0
  143. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/drift.py +0 -0
  144. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/examples.py +0 -0
  145. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/explain.py +0 -0
  146. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/metric_from.py +0 -0
  147. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/reconciliation.py +0 -0
  148. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/sensitivity.py +0 -0
  149. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/src/agentic_data_contracts/validation/validator.py +0 -0
  150. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/__init__.py +0 -0
  151. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/conftest.py +0 -0
  152. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/cube_measures.yml +0 -0
  153. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/.gitignore +0 -0
  154. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/dbt_project.yml +0 -0
  155. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/accounts.sql +0 -0
  156. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/customers.sql +0 -0
  157. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/latest_spec.yml +0 -0
  158. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/legacy_spec.yml +0 -0
  159. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/metricflow_time_spine.sql +0 -0
  160. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/orders.sql +0 -0
  161. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/models/payments.sql +0 -0
  162. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow/profiles.yml +0 -0
  163. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow_manifest.json +0 -0
  164. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/dbt_metricflow_osi_document.json +0 -0
  165. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/decomposition_source.yml +0 -0
  166. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/filter_values_contract.yml +0 -0
  167. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/minimal_contract.yml +0 -0
  168. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/missing_source_contract.yml +0 -0
  169. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/principals_contract.yml +0 -0
  170. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/relationships_checker.yml +0 -0
  171. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/relationships_preferred.yml +0 -0
  172. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/roundtrip_contract.yml +0 -0
  173. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/sample_cube_schema.yml +0 -0
  174. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/sample_cube_schema_with_joins.yml +0 -0
  175. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/sample_dbt_manifest.json +0 -0
  176. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/sample_dbt_manifest_with_relationships.json +0 -0
  177. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/sample_ossie_model.yml +0 -0
  178. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/sample_ossie_multi_model.yml +0 -0
  179. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/semantic_source.yml +0 -0
  180. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/fixtures/valid_contract.yml +0 -0
  181. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_adapters/__init__.py +0 -0
  182. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_adapters/test_duckdb.py +0 -0
  183. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_ard/__init__.py +0 -0
  184. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_ard/test_catalog_entry.py +0 -0
  185. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_bridge/__init__.py +0 -0
  186. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_bridge/test_compiler.py +0 -0
  187. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/__init__.py +0 -0
  188. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_contract.py +0 -0
  189. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_contract_allowlist_for.py +0 -0
  190. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_domain_model.py +0 -0
  191. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_freeze_fixes.py +0 -0
  192. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_load_semantic_source.py +0 -0
  193. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_principal_in_scope.py +0 -0
  194. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_principal_resolver.py +0 -0
  195. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_prompt_renderers.py +0 -0
  196. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_recorder.py +0 -0
  197. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_scalability.py +0 -0
  198. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_schema.py +0 -0
  199. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_schema_principals.py +0 -0
  200. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_sdk_config.py +0 -0
  201. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_session.py +0 -0
  202. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_staleness.py +0 -0
  203. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_system_prompt_metrics.py +0 -0
  204. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_core/test_wildcard_tables.py +0 -0
  205. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_portability/__init__.py +0 -0
  206. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_portability/test_contract_round_trip.py +0 -0
  207. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_public_api.py +0 -0
  208. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/__init__.py +0 -0
  209. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_cube.py +0 -0
  210. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_cube_measures.py +0 -0
  211. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_dbt.py +0 -0
  212. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_dbt_metricflow.py +0 -0
  213. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_decomposition.py +0 -0
  214. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_domain_membership.py +0 -0
  215. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_extras_roundtrip.py +0 -0
  216. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_from_sql.py +0 -0
  217. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_malformed_documents.py +0 -0
  218. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_metric_impacts.py +0 -0
  219. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_nested_keys.py +0 -0
  220. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_null_containers.py +0 -0
  221. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_null_text_fields.py +0 -0
  222. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_relationships.py +0 -0
  223. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_search.py +0 -0
  224. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_sensitivity.py +0 -0
  225. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_table_description.py +0 -0
  226. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_untranslated.py +0 -0
  227. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_semantic/test_yaml_source.py +0 -0
  228. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/__init__.py +0 -0
  229. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_auto_load.py +0 -0
  230. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_decomposition_tools.py +0 -0
  231. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_describe_table_description.py +0 -0
  232. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_describe_table_drift.py +0 -0
  233. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_error_response_kind.py +0 -0
  234. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_error_signalling.py +0 -0
  235. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_event_loop.py +0 -0
  236. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_execution_errors.py +0 -0
  237. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_factory.py +0 -0
  238. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_factory_principals.py +0 -0
  239. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_factory_principals_webex_scenario.py +0 -0
  240. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_fail_closed_semantic_source.py +0 -0
  241. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_inspect_query.py +0 -0
  242. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_langchain.py +0 -0
  243. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_lookup_metric_sensitivity.py +0 -0
  244. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_metric_impacts_tools.py +0 -0
  245. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_middleware.py +0 -0
  246. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_pydantic_ai.py +0 -0
  247. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_query_timeout.py +0 -0
  248. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_recorder_integration.py +0 -0
  249. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_relationship_tools.py +0 -0
  250. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_result_rows.py +0 -0
  251. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_row_format.py +0 -0
  252. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_sdk.py +0 -0
  253. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_semantic_tools.py +0 -0
  254. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_tool_protocol.py +0 -0
  255. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_tools/test_wildcard_tools.py +0 -0
  256. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/__init__.py +0 -0
  257. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_attribution.py +0 -0
  258. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_conformance.py +0 -0
  259. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_conformance_e2e.py +0 -0
  260. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_drift.py +0 -0
  261. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_drift_accounting.py +0 -0
  262. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_example_metric_refs.py +0 -0
  263. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_examples.py +0 -0
  264. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_explain.py +0 -0
  265. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_metric_from_sql.py +0 -0
  266. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_reconciliation.py +0 -0
  267. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_relationship_checker.py +0 -0
  268. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_result_checks.py +0 -0
  269. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_rows.py +0 -0
  270. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_scalar_value.py +0 -0
  271. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_sensitivity.py +0 -0
  272. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_sql_normalizer.py +0 -0
  273. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_table_allowlist_checker_principals.py +0 -0
  274. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_timewindow.py +0 -0
  275. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_validator.py +0 -0
  276. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_validator_filter_values.py +0 -0
  277. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_validator_principals.py +0 -0
  278. {agentic_data_contracts-0.62.0 → agentic_data_contracts-0.63.0}/tests/test_validation/test_validator_relative_time.py +0 -0
@@ -32,7 +32,7 @@ jobs:
32
32
  contents: read
33
33
  steps:
34
34
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
35
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
35
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
36
36
  with:
37
37
  enable-cache: true
38
38
  cache-dependency-glob: "pyproject.toml"
@@ -54,7 +54,7 @@ jobs:
54
54
  python-version: ["3.12", "3.13"]
55
55
  steps:
56
56
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
57
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
57
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
58
58
  with:
59
59
  enable-cache: true
60
60
  cache-dependency-glob: "pyproject.toml"
@@ -99,7 +99,7 @@ jobs:
99
99
  contents: read
100
100
  steps:
101
101
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
102
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
102
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
103
103
  with:
104
104
  enable-cache: true
105
105
  cache-dependency-glob: "pyproject.toml"
@@ -168,7 +168,7 @@ jobs:
168
168
  contents: read
169
169
  steps:
170
170
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
171
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
171
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
172
172
  with:
173
173
  enable-cache: true
174
174
  cache-dependency-glob: "pyproject.toml"
@@ -215,7 +215,7 @@ jobs:
215
215
  python-version: ["3.12", "3.13"]
216
216
  steps:
217
217
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
218
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
218
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
219
219
  with:
220
220
  enable-cache: true
221
221
  cache-dependency-glob: "pyproject.toml"
@@ -256,7 +256,7 @@ jobs:
256
256
  contents: read
257
257
  steps:
258
258
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
259
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
259
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
260
260
  - run: uvx uv-secure uv.lock
261
261
 
262
262
  publish:
@@ -268,9 +268,9 @@ jobs:
268
268
  id-token: write
269
269
  steps:
270
270
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
271
- - uses: astral-sh/setup-uv@c18668ad3cf93ea998bef934396af7bb5c839dc7 # v10.2.0
271
+ - uses: astral-sh/setup-uv@68559e99cc807c1eb7e9238fd43a15bba460eec2 # v10.3.3
272
272
  with:
273
273
  enable-cache: true
274
274
  - run: uv python install 3.13
275
275
  - run: uv build --no-sources
276
- - uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
276
+ - uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
@@ -32,8 +32,15 @@ docs/paper/pvldb.out
32
32
  docs/paper/pvldb.bbl
33
33
  docs/paper/pvldb.blg
34
34
 
35
- # Local git worktrees (isolated workspaces for plan execution)
35
+ # Local git worktrees (isolated workspaces for plan execution). The harness
36
+ # puts its own under .claude/worktrees/; ignoring them also keeps ty, which
37
+ # respects .gitignore, from checking a worktree's nested experiments/ copy
38
+ # against the root venv.
36
39
  .worktrees/
40
+ .claude/worktrees/
37
41
 
38
42
  # Subagent-driven-development scratch (ledgers, briefs, review packages)
39
43
  .superpowers/
44
+
45
+ # Local secrets (API tokens)
46
+ .env
@@ -1,6 +1,6 @@
1
1
  repos:
2
2
  - repo: https://github.com/astral-sh/ruff-pre-commit
3
- rev: v0.16.10
3
+ rev: v0.17.3
4
4
  hooks:
5
5
  - id: ruff-check
6
6
  args: [--fix]
@@ -11,7 +11,7 @@ repos:
11
11
  # -- a `language: system` hook calling `ty check` cannot see them, and needs
12
12
  # `unresolved-import = "ignore"` to stay quiet.
13
13
  - repo: https://github.com/astral-sh/ty-pre-commit
14
- rev: v0.0.85
14
+ rev: v0.0.95
15
15
  hooks:
16
16
  - id: ty
17
17
 
@@ -2,6 +2,37 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.63.0] - 2026-11-07
6
+
7
+ ### Fixed
8
+
9
+ - **`OssieSource` reads current-spec Ossie documents.** apache/ossie#383 made a document one model, with `name` and `datasets` at the root, and the spec no longer accepts the earlier `semantic_model` list. `OssieSource` read only that list, so a current-spec document loaded as a source with no tables, metrics or relationships, and nothing reported it. The contract's own allowlist and forbidden operations were still enforced; the agent lost the metrics and joins. NVIDIA Auto-Ontology's Ossie export is in the current shape, and its real converter output is now a test fixture. A document in the legacy shape is still read and logs a warning.
10
+ - **An Ossie table schema lists the physical columns its fields read.** A field is a name over an expression, and the field's name was registered as the column. A field `customer_name` over the column `name` registered `customer_name`, so the agent was told to select a column the table does not have, and a computed field such as `net_total` (`subtotal - discount`) was listed as a column too. The column is now the one the expression reads, in plain, backtick or bracket quoting; a computed field, a keyword such as `current_date` and an unparseable expression register none. Relationship endpoints are mapped the same way; a relationship that joins on a computed field is skipped with a warning. Metric expressions still name fields, so the bridge is in `describe_table`: a renamed column's description ends `(Ossie field: customer_name)`, and a table's lists its computed fields as `net_total = subtotal - discount`. Every fixture named its fields after their columns, so nothing caught this until a real export.
11
+ - **A metric written with only `expression.sql` has an expression.** The spec added `sql` as the primary portable expression (apache/ossie#617); such a metric loaded with an empty `sql_expression`. `sql` is read as Ossie SQL and overrides a `dialects` entry for `OSSIE_SQL_2026`. `ANSI_SQL` is still preferred over both. Dialect names, the caller's `dialect=` included, are now compared ignoring case, so the spec's `OSSIE_SQL_2026` and the proposal's `Ossie_SQL_2026` name one dialect.
12
+ - **Vendor blocks on datasets, fields, metrics and relationships are carried.** The schema allows `custom_extensions` on each, and only the model-level list was read; the rest were dropped silently. They are now in `get_extras()["ossie_entity_extensions"]`, keyed by model, entity kind, entity name, then vendor. NVIDIA Auto-Ontology puts each metric's physical SQL there. Our own block is still read only at the model level; one found on an entity is named in a warning.
13
+
14
+ ### Added
15
+
16
+ - **A BI formula is never handed to the agent as SQL.** When no preferred dialect is present, an Ossie metric fell back to its first declared expression, whatever its language, so a Tableau export's `SUM([Profit])` reached `lookup_metric` as the metric's SQL. The fallback now skips the formula languages in Ossie's dialect registry (`TABLEAU`, `DAX`, `MDX`, `MAQL`, `SIGMA`, `THOUGHTSPOT`, `HOLISTICS_AQL`, `SPARQL`); asking for one with `dialect=` still returns it, and an unregistered dialect is still a fallback. A metric written only in those is kept with an empty `sql_expression` and an `untranslated` reason quoting each formula, as dbt and Cube report a metric they cannot express. A field written only in one registers no column.
17
+ - **Dataset-scoped Ossie metrics** (`datasets[].metrics`, apache/ossie#591), which were not read. Each is named `dataset.metric`, the form the spec references it by. Our vendor block's `metrics` reaches it by that name, and a key in the bare form is named in a warning that suggests the scoped one. Its expression is passed through unchanged, as a model-scoped one's is.
18
+
19
+ ### Changed
20
+
21
+ - **An Ossie document is refused at load when it would load as less than it declares**, with a `ValueError` naming what is missing, instead of loading empty or partial. That covers a document with neither `datasets` at the root nor a `semantic_model` list, an empty `semantic_model` list, a document with both, a model with no datasets, a dataset, field or metric with no name, and a metric with no expression. A field with no expression, which the spec also requires, is still read, with its name as its column.
22
+ - **A `drill_by` in our Ossie vendor block names a physical column.** It is validated against the table schema, which now holds physical columns, so a `drill_by` written with a renamed field's name (`orders.customer_name` for a field over `cust_nm`) now fails at load with "unknown column". It named a column the table does not have; write the column instead.
23
+ - **Silent drops in an Ossie document are now named in a warning:** a key in our vendor block that matches no metric (its owners, tier and decompositions applied to nothing), a vendor listed twice in one `custom_extensions` list (the later block wins), and a duplicate metric name (lookups return the first).
24
+
25
+ ## [0.62.1] - 2026-10-16
26
+
27
+ ### Fixed
28
+
29
+ - **`blocked_columns` no longer rejects `COUNT(*)` as `SELECT *`** (#148). On any table with `blocked_columns`, a plain row count failed with "SELECT * may expose blocked columns", because the checker matched a star anywhere in the query and `COUNT(*)` contains one. The same false positive was fixed in `no_select_star` earlier, but that fix did not reach `blocked_columns`. A star as the argument of `COUNT` (`COUNT(*)`, `COUNT(t.*)`, `COUNT(DISTINCT *)`) now passes. Every other star is still rejected: `SELECT *`, `SELECT t.*`, a starred subquery, and a star inside any other function. That last case is why `no_select_star`'s rule was not reused: it allows any star that is not itself a projection, but `row_to_json(t.*)`, `ARRAY_AGG(t.*)` and `STRUCT(*)` return every column's value, blocked ones included.
30
+ - **The `blocked_columns` message no longer says `SELECT *` about a query without one.** It now reads "A `*` outside COUNT(*) may expose blocked columns: …" for every rejected star. It does not quote the query back: the checker has no dialect, and SQL rendered in sqlglot's default one can name a function the query does not contain (Snowflake's `OBJECT_CONSTRUCT(*)` renders as `STAR_MAP(*)`).
31
+
32
+ ### Documentation
33
+
34
+ - **The README says what query checks do not protect against.** They stop a cooperating agent from breaking a rule by mistake. They are not an access-control boundary: a query written to evade `blocked_columns` can still return a blocked column's values. A column that must never reach a caller needs database column privileges or a view as well.
35
+
5
36
  ## [0.62.0] - 2026-10-12
6
37
 
7
38
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentic-data-contracts
3
- Version: 0.62.0
3
+ Version: 0.63.0
4
4
  Summary: YAML-first, domain-driven data governance for AI agents
5
5
  Project-URL: Homepage, https://github.com/flyersworder/agentic-data-contracts
6
6
  Project-URL: Repository, https://github.com/flyersworder/agentic-data-contracts
@@ -114,14 +114,17 @@ That table is the claim; [`experiments/dabstep-contract-eval/`](experiments/dabs
114
114
  | `manual_prompt` | the vendor manual verbatim in the prompt | 22.9% | 42.9% | 50.3% | 23.8% |
115
115
  | `schema_only` | table and column names only | 13.9% | 22.6% | 37.0% | 22.9% |
116
116
 
117
- - **The gain is the content, not the scaffolding.** `contract_hollow` runs identical tooling with the domain knowledge stripped out. That is the only difference between the two arms, and on every model tested it explains more of the contract's lead than the tooling does.
118
- - **The contract is also the cheapest way to be right.** On every model the contract arm uses the fewest turns and by far the least reasoning; on three of four it also makes the fewest tool calls and is cheapest per correct answer (on Sonnet 5, about half the cost of any other arm). The exception is `gpt-5.6-sol`, where the contract's own text is a real input-token bill and `manual_prompt` edges it. It is the only arm that never had to be forced to answer. It is not thinking harder; it has less to search for.
119
- - **Governance held.** Across the ungoverned arms the models submitted 166 mutating SQL statements on 26 tasks. Both governed arms submitted zero.
120
- - **No leakage.** The contract was written from the vendor's manual alone and frozen, digest-pinned, before any benchmark question was read.
117
+ - **The gain is the content, not the scaffolding.** `contract_hollow` runs identical tooling with the domain knowledge stripped out. That is the only difference between the two arms, and on every model tested it explains more of the contract's lead than the tooling does. Repeated three times on two more models, emptying the contract costs 26-50 hard-task points; the tools alone are worth -9 to +15.
118
+ - **Most of the lead over the manual was knowledge the manual lacks.** Follow-up experiments found two facts the vendor manual never states (an empty list means "all", and the fees of overlapping rules add up). Stated as two notes in the prompt, they recover most of the contract's lead; with the facts stated alike, the contract's format adds about 4-6 points on the fee tasks. Write the conventions down; that is where the accuracy is.
119
+ - **Delivery buys cost and an audit trail, not much accuracy.** For one database the contract in a cached prompt is as cheap or cheaper than looking it up. From about twice that size, lookup is cheaper (1.7-2.4x at four databases' worth, 6.4x at 22) and keeps working past the context window, and the lookup log names the definition behind each answer.
120
+ - **Stated rules deterred writes.** Across the ungoverned arms of the single runs the models submitted 166 mutating SQL statements on 26 tasks; the governed arms submitted none. Across every later DABStep run, no run whose prompt stated the forbidden operations corrupted its database copy, whether the rules came through the tools or the contract's text. Serve the agent through a read-only role regardless.
121
+ - **Frozen before scoring.** The contract was written in one interactive session, a model drafting under the author's direction, from the vendor's manual, the dataset README and the data, and frozen, digest-pinned, before any benchmark score existed; the paper gives the full timeline and what it cannot prove.
121
122
  - **Externally scored.** The contract arm answered all 450 tasks and was submitted to the [DABStep leaderboard](https://huggingface.co/spaces/adyen/DABstep), where Adyen graded it against the official golds: **51.9% on the hard split** for `glm-5.3-flash`, against a published band of ~20–26% hard for manual-in-prompt baselines running stronger models.
122
123
 
123
124
  Scope, stated plainly. The effect concentrates in the domain-specific task bucket; the non-fee bucket shows no contract advantage on any model. The table's golds are reconstructed for 401 of 450 tasks, and the leaderboard check shows that scoring runs ~6 points optimistic on hard tasks, one-sidedly; correcting for it moves the arm gaps by under 3 points, in both directions. Only the contract arm was submitted, so the gap above is not graded the same way on both sides. And this is one benchmark at k=1, where a replicate finds 23% of task verdicts flip at temperature 0. Full results, every caveat, and all 6,416 transcripts: **[`FINDINGS.md`](experiments/dabstep-contract-eval/FINDINGS.md)**.
124
125
 
126
+ Archived on Zenodo for the paper: the code at release `dabstep-eval-2026-11`, [10.5281/zenodo.24015265](https://doi.org/10.5281/zenodo.24015265), and the transcripts of releases `dabstep-eval-2026-10` and `dabstep-eval-2026-11`, [10.5281/zenodo.24015263](https://doi.org/10.5281/zenodo.24015263). Paper v2 used the `dabstep-eval-2026-10` versions: code [10.5281/zenodo.23598672](https://doi.org/10.5281/zenodo.23598672), transcripts [10.5281/zenodo.23598750](https://doi.org/10.5281/zenodo.23598750).
127
+
125
128
  **Works with:** any Python agent framework — first-class helpers for the [Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk-python), [LangChain](https://github.com/langchain-ai/langchain) / [deepagents](https://github.com/langchain-ai/deepagents), and [Pydantic AI](https://github.com/pydantic/pydantic-ai), plus a framework-free path (the tools are plain async functions). Optionally integrates with [ai-agent-contracts](https://pypi.org/project/ai-agent-contracts/) for formal resource governance.
126
129
 
127
130
  > **See it running:** [three example agents](#examples) — `revenue_agent` (finance), `growth_agent` (experimentation), `ops_agent` (SRE) — each runs end-to-end in demo mode with no API key.
@@ -701,7 +704,7 @@ Either way, the agent knows that "revenue is recognized at fulfillment, not at b
701
704
 
702
705
  ### Why progressive disclosure works
703
706
 
704
- This pattern — compact index in the prompt, detailed context on demand — is the same philosophy validated by agent skill systems, MCP tool servers, and RAG architectures. Instead of overloading the agent's context window with everything upfront, you give it just enough to know *where to look*, then let it pull details when needed. The result is better token efficiency, more focused reasoning, and fewer hallucinations from context overload.
707
+ This pattern -- compact index in the prompt, detailed context on demand -- is the same philosophy behind agent skill systems, MCP tool servers, and RAG architectures. Measured on our benchmarks, its payoff is cost and traceability rather than accuracy: a contract several databases large in the prompt lost no accuracy on questions that name their metric, but on-demand lookup kept the cost per question nearly flat as the contract grew to 22 databases, ran where the full contract no longer fit the context window, and logged which definition each answer used.
705
708
 
706
709
  ## Contract Rules
707
710
 
@@ -723,6 +726,8 @@ Each rule carries a `query_check` (pre-execution) or `result_check` (post-execut
723
726
  | `require_limit` | Require a LIMIT clause |
724
727
  | `max_joins` | Cap the number of JOINs |
725
728
 
729
+ Query checks read the SQL text before it runs. They keep a cooperating agent from doing by mistake what a rule forbids, and tell it why, so it can correct the query. They are not an access-control boundary: SQL has more ways to route a value into a result than a static check can recognise, so a query written to evade `blocked_columns` can still return a blocked column's values. If a column must never reach a given caller, enforce that in the database as well, with column privileges or a view that leaves it out, and connect the agent with credentials that have only that access.
730
+
726
731
  **Built-in result checks** (post-execution, validated against query output):
727
732
 
728
733
  | Check | Description |
@@ -943,15 +948,20 @@ semantic:
943
948
  path: "./semantic/model.yml"
944
949
  ```
945
950
 
946
- Ossie standardises what a metric *is*; this library enforces what an agent may *do* with it. So the spec is a strict subset of the vocabulary here — an Ossie `Metric` carries only `name`, `expression`, `description`, `datatype`, and `ai_context`.
951
+ Ossie standardises what a metric *is*; this library enforces what an agent may *do* with it. So the spec is a strict subset of the vocabulary here — an Ossie `Metric` carries only `name`, `expression`, `description`, `datatype`, `ai_context`, and `custom_extensions`.
952
+
953
+ A document is one model, with its `name`, `datasets`, `relationships` and `metrics` at the root, as the current spec defines it. A file in the earlier shape, with models in a `semantic_model` list, is still read and logs a warning saying it is legacy. Nothing in a model is dropped silently: a file with neither shape or a model with no datasets is refused at load, as is a dataset, field or metric with no name or a metric with no expression, rather than loading as less than it declares.
947
954
 
948
955
  | Ossie | Read as |
949
956
  | --- | --- |
950
- | `datasets[].source` + `fields[]` | Table schemas. A three-part `database.schema.table` is keyed on its trailing `schema.table`; a query-backed dataset registers no table |
951
- | `metrics[].expression.dialects[]` | `sql_expression`, resolved deterministically — your `dialect=` first, then `ANSI_SQL`, then `Ossie_SQL_2026`, then the first declared entry |
952
- | `relationships[]` | `Relationship`, with cardinality *derived from both sides* — see below |
957
+ | `datasets[].source` + `fields[]` | Table schemas of *physical* columns. A field's column is the one its expression reads, in any identifier quoting: a field `customer_name` over `name` registers `name`, and a computed field (`subtotal - discount`) registers none. Because metric expressions still name fields, `describe_table` carries the bridge: the column's description ends `(Ossie field: customer_name)`, and the table's lists its computed fields as `net_total = subtotal - discount`. A `drill_by` in our vendor block names the physical column. A three-part `database.schema.table` is keyed on its trailing `schema.table`; a query-backed dataset registers no table |
958
+ | `metrics[].expression` | `sql_expression`, resolved deterministically — your `dialect=` first, then `ANSI_SQL`, then Ossie SQL (`expression.sql`, which overrides a `dialects` entry for `OSSIE_SQL_2026`), then the first declared entry that is SQL. Dialect names are matched ignoring case. A formula language from Ossie's dialect registry (`TABLEAU`, `DAX`, `MDX`, `MAQL`, `SIGMA`, `THOUGHTSPOT`, `HOLISTICS_AQL`, `SPARQL`) is never the fallback, though you get it if you ask for it with `dialect=`. A metric written only in those keeps an empty `sql_expression`, and its `untranslated` reason, which `lookup_metric` shows the agent, quotes the formula |
959
+ | `datasets[].metrics[]` | A metric named `dataset.metric`, as the spec references it |
960
+ | `relationships[]` | `Relationship` between the physical columns its fields read, with cardinality *derived from both sides* — see below |
953
961
  | `ai_context` (anywhere) | Carried into `get_extras()["ossie_ai_context"]`, keyed by model then entity kind, never interpreted |
954
- | `custom_extensions[]` | Ours read as real vocabulary; every other vendor's carried into `get_extras()["ossie_custom_extensions"]`, keyed by model then vendor |
962
+ | `custom_extensions[]` | Ours, at the model level, read as real vocabulary. Every other vendor's carried: model-level blocks into `get_extras()["ossie_custom_extensions"]` (by model, then vendor), and blocks on a dataset, field, metric or relationship into `get_extras()["ossie_entity_extensions"]` (by model, entity kind, entity name, then vendor) |
963
+
964
+ Metric expressions, of either scope, are passed through unchanged. They name the model's datasets and fields, which may be renamed or computed, not physical columns, so Ossie gives a metric no `source_model`: set one in our vendor block when the expression does read one table as written.
955
965
 
956
966
  **Cardinality.** Ossie never writes the join type down; it is implied by which endpoints are keys. The spec documents `to` as the one side, but nothing validates that — and trusting it is not free, because `RelationshipChecker` fires its fan-out warning only on `one_to_many`. So both sides are checked:
957
967
 
@@ -966,41 +976,41 @@ Keys are optional in Ossie, and their absence is not evidence of fan-out — whe
966
976
 
967
977
  Composite-key relationships are **skipped with a warning** rather than split into one edge per column pair — our `Relationship` has single-column endpoints, and splitting would assert two joins that are each individually wrong. Declare those in a `YamlSource` overlay.
968
978
 
969
- **Governance fields.** Ownership, review dates, tiers, decompositions, and the metric-impact graph have no home in the Ossie spec, and every `$def` in its JSON Schema sets `additionalProperties: false` — so they cannot be added as extra keys. They ride in the spec's own escape hatch, `custom_extensions`, under this project's vendor name:
979
+ **Governance fields.** Ownership, review dates, tiers, decompositions, and the metric-impact graph have no home in the Ossie spec, and every entity in its JSON Schema (model, dataset, field, metric, relationship) sets `additionalProperties: false` — so they cannot be added as extra keys. They ride in the spec's own escape hatch, `custom_extensions`, under this project's vendor name, in the model-level block. Metrics are keyed by name, a dataset-scoped one as `dataset.metric`:
970
980
 
971
981
  ```yaml
972
- custom_extensions:
973
- - vendor_name: AGENTIC_DATA_CONTRACTS
974
- data: |
975
- {
976
- "metrics": {
977
- "total_sales": {
978
- "business_owner": "revenue-analytics",
979
- "operational_owner": "data-platform",
980
- "last_reviewed": "2026-05-01",
981
- "tier": ["gold"],
982
- "domains": ["sales"],
983
- "decompositions": [
984
- {"operator": "sum", "operands": ["total_profit", "total_cost"]}
985
- ],
986
- "drill_by": [
987
- {"dimension": "region", "column": "public.customer.c_region"}
988
- ]
989
- }
990
- },
991
- "metric_impacts": [
992
- {"from": "total_cost", "to": "total_profit",
993
- "direction": "negative", "confidence": "verified",
994
- "evidence": "Margin bridge reconciled 2026-Q1."}
982
+ custom_extensions:
983
+ - vendor_name: AGENTIC_DATA_CONTRACTS
984
+ data: |
985
+ {
986
+ "metrics": {
987
+ "total_sales": {
988
+ "business_owner": "revenue-analytics",
989
+ "operational_owner": "data-platform",
990
+ "last_reviewed": "2026-05-01",
991
+ "tier": ["gold"],
992
+ "domains": ["sales"],
993
+ "decompositions": [
994
+ {"operator": "sum", "operands": ["total_profit", "total_cost"]}
995
+ ],
996
+ "drill_by": [
997
+ {"dimension": "region", "column": "public.customer.c_region"}
995
998
  ]
996
999
  }
1000
+ },
1001
+ "metric_impacts": [
1002
+ {"from": "total_cost", "to": "total_profit",
1003
+ "direction": "negative", "confidence": "verified",
1004
+ "evidence": "Margin bridge reconciled 2026-Q1."}
1005
+ ]
1006
+ }
997
1007
  ```
998
1008
 
999
1009
  Ossie stores extension payloads as a JSON *string*, so `data` is JSON nested inside YAML (a plain YAML mapping is accepted too). Restored decompositions and `drill_by` go through the same validators as `YamlSource`, failing loudly at load, and a bare `"tier": "gold"` is promoted to a list exactly as `YamlSource` does. Another vendor's malformed payload is carried verbatim and logged rather than raised on — a neighbour's typo must not stop your contract from being enforced.
1000
1010
 
1001
1011
  `expected_extras` does not apply to an Ossie source: its extras are synthesized by the parser under fixed keys, not authored as free top-level sections. Declaring it on a non-`yaml` source logs a warning rather than being silently dropped.
1002
1012
 
1003
- > The spec is at `0.2.0.dev0` with no tagged releases yet, so expect churn. `dialect` is deliberately treated as an opaque string and never validated against the enum, since the accepted expression-language proposal adds `Ossie_SQL_2026` and makes it the default.
1013
+ > The spec is at `0.2.0.dev0` with no tagged releases yet, so expect churn. `dialect` is deliberately treated as an opaque string and never validated, as the spec itself accepts any non-empty string. Producers that export Ossie, such as NVIDIA Auto-Ontology's Import / Export, are read directly; `tests/fixtures/ossie_flat_nvidia_auto_ontology.yml` is that converter's real output. It is a record of what a producer emits, not a spec-valid example: its metric reads `orders.subtotal`, a column that no field declares, which the spec's name-resolution rules do not allow.
1004
1014
 
1005
1015
  ## Table Relationships
1006
1016
 
@@ -1990,6 +2000,8 @@ DATA_PLUGIN_PATH=/tmp/kwp/data \
1990
2000
 
1991
2001
  **Do I have to use dbt or Cube?** No. Author metrics inline in a `semantic.yml` with `YamlSource`. dbt (`manifest.json`), Cube, and Apache Ossie are supported if you already have them — the agent-facing behavior is identical regardless of source.
1992
2002
 
2003
+ **How does this compare to WrenAI?** [WrenAI](https://github.com/Canner/WrenAI) is a governed text-to-SQL *engine*: you model your data in its MDL semantic layer, and its Apache DataFusion-based engine plans each agent query against that model, dry-plan validates it, and executes it across its supported sources. This library has no engine. It checks the SQL the agent writes against a YAML contract, then passes the query to your own database through a `DatabaseAdapter` as written, and it reads metric definitions from the semantic source you already maintain (dbt, Cube, Apache Ossie, or inline YAML). Pick Wren if you want one engine to own both modelling and execution; pick this library if your warehouse and semantic layer stay as they are and you need governance in front of them. One licensing difference: per-caller table and column rules ([`caller_principal`](#per-caller-access-control-optional)) are part of this MIT library, while Wren lists row- and column-level security as a commercial feature.
2004
+
1993
2005
  **Does it work without the Claude Agent SDK?** Yes. The tools are plain async functions usable from LangChain/deepagents, Pydantic AI, or directly; the example agents fall back to a no-SDK demo mode.
1994
2006
 
1995
2007
  **Is it production-ready?** It's pre-1.0 and actively evolving. Every breaking change is documented in [`CHANGELOG.md`](CHANGELOG.md) with migration notes.
@@ -42,14 +42,17 @@ That table is the claim; [`experiments/dabstep-contract-eval/`](experiments/dabs
42
42
  | `manual_prompt` | the vendor manual verbatim in the prompt | 22.9% | 42.9% | 50.3% | 23.8% |
43
43
  | `schema_only` | table and column names only | 13.9% | 22.6% | 37.0% | 22.9% |
44
44
 
45
- - **The gain is the content, not the scaffolding.** `contract_hollow` runs identical tooling with the domain knowledge stripped out. That is the only difference between the two arms, and on every model tested it explains more of the contract's lead than the tooling does.
46
- - **The contract is also the cheapest way to be right.** On every model the contract arm uses the fewest turns and by far the least reasoning; on three of four it also makes the fewest tool calls and is cheapest per correct answer (on Sonnet 5, about half the cost of any other arm). The exception is `gpt-5.6-sol`, where the contract's own text is a real input-token bill and `manual_prompt` edges it. It is the only arm that never had to be forced to answer. It is not thinking harder; it has less to search for.
47
- - **Governance held.** Across the ungoverned arms the models submitted 166 mutating SQL statements on 26 tasks. Both governed arms submitted zero.
48
- - **No leakage.** The contract was written from the vendor's manual alone and frozen, digest-pinned, before any benchmark question was read.
45
+ - **The gain is the content, not the scaffolding.** `contract_hollow` runs identical tooling with the domain knowledge stripped out. That is the only difference between the two arms, and on every model tested it explains more of the contract's lead than the tooling does. Repeated three times on two more models, emptying the contract costs 26-50 hard-task points; the tools alone are worth -9 to +15.
46
+ - **Most of the lead over the manual was knowledge the manual lacks.** Follow-up experiments found two facts the vendor manual never states (an empty list means "all", and the fees of overlapping rules add up). Stated as two notes in the prompt, they recover most of the contract's lead; with the facts stated alike, the contract's format adds about 4-6 points on the fee tasks. Write the conventions down; that is where the accuracy is.
47
+ - **Delivery buys cost and an audit trail, not much accuracy.** For one database the contract in a cached prompt is as cheap or cheaper than looking it up. From about twice that size, lookup is cheaper (1.7-2.4x at four databases' worth, 6.4x at 22) and keeps working past the context window, and the lookup log names the definition behind each answer.
48
+ - **Stated rules deterred writes.** Across the ungoverned arms of the single runs the models submitted 166 mutating SQL statements on 26 tasks; the governed arms submitted none. Across every later DABStep run, no run whose prompt stated the forbidden operations corrupted its database copy, whether the rules came through the tools or the contract's text. Serve the agent through a read-only role regardless.
49
+ - **Frozen before scoring.** The contract was written in one interactive session, a model drafting under the author's direction, from the vendor's manual, the dataset README and the data, and frozen, digest-pinned, before any benchmark score existed; the paper gives the full timeline and what it cannot prove.
49
50
  - **Externally scored.** The contract arm answered all 450 tasks and was submitted to the [DABStep leaderboard](https://huggingface.co/spaces/adyen/DABstep), where Adyen graded it against the official golds: **51.9% on the hard split** for `glm-5.3-flash`, against a published band of ~20–26% hard for manual-in-prompt baselines running stronger models.
50
51
 
51
52
  Scope, stated plainly. The effect concentrates in the domain-specific task bucket; the non-fee bucket shows no contract advantage on any model. The table's golds are reconstructed for 401 of 450 tasks, and the leaderboard check shows that scoring runs ~6 points optimistic on hard tasks, one-sidedly; correcting for it moves the arm gaps by under 3 points, in both directions. Only the contract arm was submitted, so the gap above is not graded the same way on both sides. And this is one benchmark at k=1, where a replicate finds 23% of task verdicts flip at temperature 0. Full results, every caveat, and all 6,416 transcripts: **[`FINDINGS.md`](experiments/dabstep-contract-eval/FINDINGS.md)**.
52
53
 
54
+ Archived on Zenodo for the paper: the code at release `dabstep-eval-2026-11`, [10.5281/zenodo.24015265](https://doi.org/10.5281/zenodo.24015265), and the transcripts of releases `dabstep-eval-2026-10` and `dabstep-eval-2026-11`, [10.5281/zenodo.24015263](https://doi.org/10.5281/zenodo.24015263). Paper v2 used the `dabstep-eval-2026-10` versions: code [10.5281/zenodo.23598672](https://doi.org/10.5281/zenodo.23598672), transcripts [10.5281/zenodo.23598750](https://doi.org/10.5281/zenodo.23598750).
55
+
53
56
  **Works with:** any Python agent framework — first-class helpers for the [Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk-python), [LangChain](https://github.com/langchain-ai/langchain) / [deepagents](https://github.com/langchain-ai/deepagents), and [Pydantic AI](https://github.com/pydantic/pydantic-ai), plus a framework-free path (the tools are plain async functions). Optionally integrates with [ai-agent-contracts](https://pypi.org/project/ai-agent-contracts/) for formal resource governance.
54
57
 
55
58
  > **See it running:** [three example agents](#examples) — `revenue_agent` (finance), `growth_agent` (experimentation), `ops_agent` (SRE) — each runs end-to-end in demo mode with no API key.
@@ -629,7 +632,7 @@ Either way, the agent knows that "revenue is recognized at fulfillment, not at b
629
632
 
630
633
  ### Why progressive disclosure works
631
634
 
632
- This pattern — compact index in the prompt, detailed context on demand — is the same philosophy validated by agent skill systems, MCP tool servers, and RAG architectures. Instead of overloading the agent's context window with everything upfront, you give it just enough to know *where to look*, then let it pull details when needed. The result is better token efficiency, more focused reasoning, and fewer hallucinations from context overload.
635
+ This pattern -- compact index in the prompt, detailed context on demand -- is the same philosophy behind agent skill systems, MCP tool servers, and RAG architectures. Measured on our benchmarks, its payoff is cost and traceability rather than accuracy: a contract several databases large in the prompt lost no accuracy on questions that name their metric, but on-demand lookup kept the cost per question nearly flat as the contract grew to 22 databases, ran where the full contract no longer fit the context window, and logged which definition each answer used.
633
636
 
634
637
  ## Contract Rules
635
638
 
@@ -651,6 +654,8 @@ Each rule carries a `query_check` (pre-execution) or `result_check` (post-execut
651
654
  | `require_limit` | Require a LIMIT clause |
652
655
  | `max_joins` | Cap the number of JOINs |
653
656
 
657
+ Query checks read the SQL text before it runs. They keep a cooperating agent from doing by mistake what a rule forbids, and tell it why, so it can correct the query. They are not an access-control boundary: SQL has more ways to route a value into a result than a static check can recognise, so a query written to evade `blocked_columns` can still return a blocked column's values. If a column must never reach a given caller, enforce that in the database as well, with column privileges or a view that leaves it out, and connect the agent with credentials that have only that access.
658
+
654
659
  **Built-in result checks** (post-execution, validated against query output):
655
660
 
656
661
  | Check | Description |
@@ -871,15 +876,20 @@ semantic:
871
876
  path: "./semantic/model.yml"
872
877
  ```
873
878
 
874
- Ossie standardises what a metric *is*; this library enforces what an agent may *do* with it. So the spec is a strict subset of the vocabulary here — an Ossie `Metric` carries only `name`, `expression`, `description`, `datatype`, and `ai_context`.
879
+ Ossie standardises what a metric *is*; this library enforces what an agent may *do* with it. So the spec is a strict subset of the vocabulary here — an Ossie `Metric` carries only `name`, `expression`, `description`, `datatype`, `ai_context`, and `custom_extensions`.
880
+
881
+ A document is one model, with its `name`, `datasets`, `relationships` and `metrics` at the root, as the current spec defines it. A file in the earlier shape, with models in a `semantic_model` list, is still read and logs a warning saying it is legacy. Nothing in a model is dropped silently: a file with neither shape or a model with no datasets is refused at load, as is a dataset, field or metric with no name or a metric with no expression, rather than loading as less than it declares.
875
882
 
876
883
  | Ossie | Read as |
877
884
  | --- | --- |
878
- | `datasets[].source` + `fields[]` | Table schemas. A three-part `database.schema.table` is keyed on its trailing `schema.table`; a query-backed dataset registers no table |
879
- | `metrics[].expression.dialects[]` | `sql_expression`, resolved deterministically — your `dialect=` first, then `ANSI_SQL`, then `Ossie_SQL_2026`, then the first declared entry |
880
- | `relationships[]` | `Relationship`, with cardinality *derived from both sides* — see below |
885
+ | `datasets[].source` + `fields[]` | Table schemas of *physical* columns. A field's column is the one its expression reads, in any identifier quoting: a field `customer_name` over `name` registers `name`, and a computed field (`subtotal - discount`) registers none. Because metric expressions still name fields, `describe_table` carries the bridge: the column's description ends `(Ossie field: customer_name)`, and the table's lists its computed fields as `net_total = subtotal - discount`. A `drill_by` in our vendor block names the physical column. A three-part `database.schema.table` is keyed on its trailing `schema.table`; a query-backed dataset registers no table |
886
+ | `metrics[].expression` | `sql_expression`, resolved deterministically — your `dialect=` first, then `ANSI_SQL`, then Ossie SQL (`expression.sql`, which overrides a `dialects` entry for `OSSIE_SQL_2026`), then the first declared entry that is SQL. Dialect names are matched ignoring case. A formula language from Ossie's dialect registry (`TABLEAU`, `DAX`, `MDX`, `MAQL`, `SIGMA`, `THOUGHTSPOT`, `HOLISTICS_AQL`, `SPARQL`) is never the fallback, though you get it if you ask for it with `dialect=`. A metric written only in those keeps an empty `sql_expression`, and its `untranslated` reason, which `lookup_metric` shows the agent, quotes the formula |
887
+ | `datasets[].metrics[]` | A metric named `dataset.metric`, as the spec references it |
888
+ | `relationships[]` | `Relationship` between the physical columns its fields read, with cardinality *derived from both sides* — see below |
881
889
  | `ai_context` (anywhere) | Carried into `get_extras()["ossie_ai_context"]`, keyed by model then entity kind, never interpreted |
882
- | `custom_extensions[]` | Ours read as real vocabulary; every other vendor's carried into `get_extras()["ossie_custom_extensions"]`, keyed by model then vendor |
890
+ | `custom_extensions[]` | Ours, at the model level, read as real vocabulary. Every other vendor's carried: model-level blocks into `get_extras()["ossie_custom_extensions"]` (by model, then vendor), and blocks on a dataset, field, metric or relationship into `get_extras()["ossie_entity_extensions"]` (by model, entity kind, entity name, then vendor) |
891
+
892
+ Metric expressions, of either scope, are passed through unchanged. They name the model's datasets and fields, which may be renamed or computed, not physical columns, so Ossie gives a metric no `source_model`: set one in our vendor block when the expression does read one table as written.
883
893
 
884
894
  **Cardinality.** Ossie never writes the join type down; it is implied by which endpoints are keys. The spec documents `to` as the one side, but nothing validates that — and trusting it is not free, because `RelationshipChecker` fires its fan-out warning only on `one_to_many`. So both sides are checked:
885
895
 
@@ -894,41 +904,41 @@ Keys are optional in Ossie, and their absence is not evidence of fan-out — whe
894
904
 
895
905
  Composite-key relationships are **skipped with a warning** rather than split into one edge per column pair — our `Relationship` has single-column endpoints, and splitting would assert two joins that are each individually wrong. Declare those in a `YamlSource` overlay.
896
906
 
897
- **Governance fields.** Ownership, review dates, tiers, decompositions, and the metric-impact graph have no home in the Ossie spec, and every `$def` in its JSON Schema sets `additionalProperties: false` — so they cannot be added as extra keys. They ride in the spec's own escape hatch, `custom_extensions`, under this project's vendor name:
907
+ **Governance fields.** Ownership, review dates, tiers, decompositions, and the metric-impact graph have no home in the Ossie spec, and every entity in its JSON Schema (model, dataset, field, metric, relationship) sets `additionalProperties: false` — so they cannot be added as extra keys. They ride in the spec's own escape hatch, `custom_extensions`, under this project's vendor name, in the model-level block. Metrics are keyed by name, a dataset-scoped one as `dataset.metric`:
898
908
 
899
909
  ```yaml
900
- custom_extensions:
901
- - vendor_name: AGENTIC_DATA_CONTRACTS
902
- data: |
903
- {
904
- "metrics": {
905
- "total_sales": {
906
- "business_owner": "revenue-analytics",
907
- "operational_owner": "data-platform",
908
- "last_reviewed": "2026-05-01",
909
- "tier": ["gold"],
910
- "domains": ["sales"],
911
- "decompositions": [
912
- {"operator": "sum", "operands": ["total_profit", "total_cost"]}
913
- ],
914
- "drill_by": [
915
- {"dimension": "region", "column": "public.customer.c_region"}
916
- ]
917
- }
918
- },
919
- "metric_impacts": [
920
- {"from": "total_cost", "to": "total_profit",
921
- "direction": "negative", "confidence": "verified",
922
- "evidence": "Margin bridge reconciled 2026-Q1."}
910
+ custom_extensions:
911
+ - vendor_name: AGENTIC_DATA_CONTRACTS
912
+ data: |
913
+ {
914
+ "metrics": {
915
+ "total_sales": {
916
+ "business_owner": "revenue-analytics",
917
+ "operational_owner": "data-platform",
918
+ "last_reviewed": "2026-05-01",
919
+ "tier": ["gold"],
920
+ "domains": ["sales"],
921
+ "decompositions": [
922
+ {"operator": "sum", "operands": ["total_profit", "total_cost"]}
923
+ ],
924
+ "drill_by": [
925
+ {"dimension": "region", "column": "public.customer.c_region"}
923
926
  ]
924
927
  }
928
+ },
929
+ "metric_impacts": [
930
+ {"from": "total_cost", "to": "total_profit",
931
+ "direction": "negative", "confidence": "verified",
932
+ "evidence": "Margin bridge reconciled 2026-Q1."}
933
+ ]
934
+ }
925
935
  ```
926
936
 
927
937
  Ossie stores extension payloads as a JSON *string*, so `data` is JSON nested inside YAML (a plain YAML mapping is accepted too). Restored decompositions and `drill_by` go through the same validators as `YamlSource`, failing loudly at load, and a bare `"tier": "gold"` is promoted to a list exactly as `YamlSource` does. Another vendor's malformed payload is carried verbatim and logged rather than raised on — a neighbour's typo must not stop your contract from being enforced.
928
938
 
929
939
  `expected_extras` does not apply to an Ossie source: its extras are synthesized by the parser under fixed keys, not authored as free top-level sections. Declaring it on a non-`yaml` source logs a warning rather than being silently dropped.
930
940
 
931
- > The spec is at `0.2.0.dev0` with no tagged releases yet, so expect churn. `dialect` is deliberately treated as an opaque string and never validated against the enum, since the accepted expression-language proposal adds `Ossie_SQL_2026` and makes it the default.
941
+ > The spec is at `0.2.0.dev0` with no tagged releases yet, so expect churn. `dialect` is deliberately treated as an opaque string and never validated, as the spec itself accepts any non-empty string. Producers that export Ossie, such as NVIDIA Auto-Ontology's Import / Export, are read directly; `tests/fixtures/ossie_flat_nvidia_auto_ontology.yml` is that converter's real output. It is a record of what a producer emits, not a spec-valid example: its metric reads `orders.subtotal`, a column that no field declares, which the spec's name-resolution rules do not allow.
932
942
 
933
943
  ## Table Relationships
934
944
 
@@ -1918,6 +1928,8 @@ DATA_PLUGIN_PATH=/tmp/kwp/data \
1918
1928
 
1919
1929
  **Do I have to use dbt or Cube?** No. Author metrics inline in a `semantic.yml` with `YamlSource`. dbt (`manifest.json`), Cube, and Apache Ossie are supported if you already have them — the agent-facing behavior is identical regardless of source.
1920
1930
 
1931
+ **How does this compare to WrenAI?** [WrenAI](https://github.com/Canner/WrenAI) is a governed text-to-SQL *engine*: you model your data in its MDL semantic layer, and its Apache DataFusion-based engine plans each agent query against that model, dry-plan validates it, and executes it across its supported sources. This library has no engine. It checks the SQL the agent writes against a YAML contract, then passes the query to your own database through a `DatabaseAdapter` as written, and it reads metric definitions from the semantic source you already maintain (dbt, Cube, Apache Ossie, or inline YAML). Pick Wren if you want one engine to own both modelling and execution; pick this library if your warehouse and semantic layer stay as they are and you need governance in front of them. One licensing difference: per-caller table and column rules ([`caller_principal`](#per-caller-access-control-optional)) are part of this MIT library, while Wren lists row- and column-level security as a commercial feature.
1932
+
1921
1933
  **Does it work without the Claude Agent SDK?** Yes. The tools are plain async functions usable from LangChain/deepagents, Pydantic AI, or directly; the example agents fall back to a no-SDK demo mode.
1922
1934
 
1923
1935
  **Is it production-ready?** It's pre-1.0 and actively evolving. Every breaking change is documented in [`CHANGELOG.md`](CHANGELOG.md) with migration notes.
@@ -893,7 +893,7 @@ class SemanticSource(Protocol):
893
893
 
894
894
  **Cube relationship parsing (v0.18.0+):** `CubeSource.get_relationships()` parses each cube's `joins:` block. The parser builds a `cube_name -> sql_table` map, regexes the single-equality form `{X}.col1 = {Y}.col2` from each join's `sql:` field, and normalises so the `from` side is always the column on the cube declaring the join (independent of which side `{CUBE}` appears on in the SQL). Cube's `relationship` enum (`belongsTo` / `hasOne` / `hasMany` plus `many_to_one` / `one_to_one` / `one_to_many` aliases) maps to canonical `Relationship.type` strings; `meta.relationship_type` overrides. `meta.preferred` and `meta.required_filter` work the same way as `YamlSource` and `DbtSource`. Composite-key joins (multiple `AND`-chained equalities) and joins whose target cube can't be resolved by name are skipped — declare those in contract YAML via `YamlSource` instead.
895
895
 
896
- **Ossie parsing (v0.42.0+):** `OssieSource` reads an [Apache Ossie (incubating)](https://ossie.apache.org/) semantic model — the vendor-neutral spec, formerly Open Semantic Interchange, now governed by the ASF. The spec is a strict subset of this library's vocabulary: an Ossie `Metric` is `name` + multi-dialect `expression` + `description` + `datatype` + `ai_context`, and nothing else. Four decisions define the adapter.
896
+ **Ossie parsing (v0.42.0+):** `OssieSource` reads an [Apache Ossie (incubating)](https://ossie.apache.org/) semantic model — the vendor-neutral spec, formerly Open Semantic Interchange, now governed by the ASF. The spec is a strict subset of this library's vocabulary: an Ossie `Metric` is `name` + `expression` (portable `sql` and/or per-dialect entries) + `description` + `datatype` + `ai_context` + `custom_extensions`, and nothing else. Four decisions define the adapter.
897
897
 
898
898
  *Table keys drop the database qualifier.* Ossie sources are `database.schema.table`; our keys are two-part because `Relationship` endpoints are `schema.table.column` and `build_relationship_index` recovers the table with a single `rsplit`. A three-part key would make every endpoint four deep and break that contract. Collisions (two databases sharing a schema and table name) are logged. Query-backed datasets register no table at all.
899
899
 
@@ -901,11 +901,17 @@ class SemanticSource(Protocol):
901
901
 
902
902
  *Composite joins are skipped, not split.* Ossie relationships carry parallel `from_columns` / `to_columns` lists; our `Relationship` has single-column endpoints. Emitting one edge per column pair would assert two joins that are each individually wrong, and the join planner walks those edges — so a composite relationship is skipped with a warning naming it, matching the precedent `CubeSource` set for Cube's `AND`-chained joins.
903
903
 
904
- *Governance rides in `custom_extensions`.* Every `$def` in `osi-schema.json` sets `additionalProperties: false` and `version` is a `const`, so ownership, review dates, tiers, `decompositions`, `drill_by`, and the impact graph cannot be added as extra keys. They live under the `AGENTIC_DATA_CONTRACTS` vendor block, whose `data` is a JSON *string* per the spec. Restored `decompositions` / `drill_by` pass through the same `validate_decompositions` / `validate_drill_by` as `YamlSource`. Scalar `tier` / `domains` / `filters` are promoted to one-element lists exactly as `YamlSource` does, since `list("gold")` would otherwise yield `['g', 'o', 'l', 'd']` and then drive tier policy.
904
+ *Governance rides in `custom_extensions`.* Every entity in `ossie-schema.json` (model, dataset, field, metric, relationship) sets `additionalProperties: false` and `version` is a `const`, so ownership, review dates, tiers, `decompositions`, `drill_by`, and the impact graph cannot be added as extra keys. They live under the `AGENTIC_DATA_CONTRACTS` vendor block, whose `data` is a JSON *string* per the spec. Restored `decompositions` / `drill_by` pass through the same `validate_decompositions` / `validate_drill_by` as `YamlSource`. Scalar `tier` / `domains` / `filters` are promoted to one-element lists exactly as `YamlSource` does, since `list("gold")` would otherwise yield `['g', 'o', 'l', 'd']` and then drive tier policy.
905
905
 
906
- Every *other* vendor's block, plus every `ai_context` in the file, is carried through `get_extras()` under `ossie_custom_extensions` / `ossie_ai_context` — the same boundary `YamlSource` draws, where the framework carries extras and renders them on request but never interprets them. Both are keyed by **semantic-model name first**: Ossie's top level is a list and it namespaces entity names per model, so a flat key would silently drop the first of two `customer` datasets or two blocks from the same vendor. Both sections are omitted entirely when empty rather than emitted as `{}`, which would add a noise key to every Ossie contract's canonical bytes and trip a contract declaring `expected_extras` on rehydrate; both are put through the shared `jsonify_extras` (promoted from `yaml_source` alongside `parse_review_date`), so a YAML-native date in an `ai_context` cannot reach `contract_canonical_bytes` unconverted. A foreign vendor's malformed JSON is carried verbatim and logged rather than raised on.
906
+ Every *other* vendor's block, plus every `ai_context` in the file, is carried through `get_extras()` under `ossie_custom_extensions` (model-level blocks), `ossie_entity_extensions` (blocks on a dataset, field, metric or relationship, keyed by entity kind and name) and `ossie_ai_context` — the same boundary `YamlSource` draws, where the framework carries extras and renders them on request but never interprets them. Our own block is read only at the model level, where it is keyed by metric name; one found on an entity is named in a warning and ignored. All three are keyed by **model name first**: a legacy document's top level is a list and Ossie namespaces entity names per model, so a flat key would silently drop the first of two `customer` datasets or two blocks from the same vendor. Both sections are omitted entirely when empty rather than emitted as `{}`, which would add a noise key to every Ossie contract's canonical bytes and trip a contract declaring `expected_extras` on rehydrate; both are put through the shared `jsonify_extras` (promoted from `yaml_source` alongside `parse_review_date`), so a YAML-native date in an `ai_context` cannot reach `contract_canonical_bytes` unconverted. A foreign vendor's malformed JSON is carried verbatim and logged rather than raised on.
907
907
 
908
- The spec is `0.2.0.dev0` with no tagged releases (apache/ossie#102), and the accepted expression-language proposal introduces an `Ossie_SQL_2026` dialect absent from today's enum. `dialect` is therefore treated as an opaque string and never validated against the enum: a closed check would break on the next spec bump for no benefit. Dialect resolution is deterministic, as the spec requires — caller's choice, then `ANSI_SQL`, then `Ossie_SQL_2026`, then the first declared entry, so a model written only in a vendor dialect still yields an expression rather than a silent empty string.
908
+ The spec is `0.2.0.dev0` with no tagged releases (apache/ossie#102), and it changes underneath us. `dialect` is treated as an opaque string and never validated, which the spec now also does: any non-empty string is accepted. Dialect resolution is deterministic, as the spec requires — caller's choice, then `ANSI_SQL`, then Ossie SQL, then the first declared entry, so a model written only in a vendor dialect still yields an expression rather than a silent empty string. `ANSI_SQL` stays ahead of Ossie SQL because the expression goes to an agent that runs it on a warehouse, and ANSI is the declared dialect closest to that; Ossie SQL is the spec's own portable language, which a consumer is expected to translate. The spec allows either ranking, as long as it is deterministic. Ossie SQL is `expression.sql` (apache/ossie#617), which overrides a `dialects` entry for `OSSIE_SQL_2026`. Dialect names are compared case-folded, the caller's included, so the proposal's `Ossie_SQL_2026` spelling and the spec's name one dialect. The last resort, the first declared entry, skips the formula languages in Ossie's dialect registry (`TABLEAU`, `DAX`, `MDX`, `MAQL`, `SIGMA`, `THOUGHTSPOT`, `HOLISTICS_AQL`, `SPARQL`): handed to the agent as SQL, `SUM([Profit])` is a query it would run or try to repair. The list names what is known not to be SQL rather than what is, because a miss is cheaper that way round: an unregistered vendor dialect such as `DENODO` may well be SQL and is still a fallback. A metric written only in formula languages is valid Ossie with no SQL to run, so it is kept the way `DbtSource` and `CubeSource` keep a metric they cannot express (#123): an empty `sql_expression` and an `untranslated` reason quoting each formula with its dialect. A field written only in one registers no column, like a computed field.
909
+
910
+ **Ossie document shape (v0.63.0+).** apache/ossie#383 made a document one model, with `name` and `datasets` at the root, and the schema now rejects the earlier `semantic_model` list. `OssieSource` read only that list, so a current-spec document, such as NVIDIA Auto-Ontology's export, loaded as a source with no tables, metrics or relationships, and nothing reported it. Both shapes are now read, the legacy one with a warning. Every way of finding no model is an error: a document with neither shape, an empty list, both shapes at once (where reading either would drop the other's models), or a model with no datasets. An empty source enforces nothing the agent can see, and nothing downstream can tell it apart from a model that simply has no metrics. The same rule reaches inside a model: a dataset, field or metric with no name, or a metric with no expression, is refused at load instead of being skipped, and a vendor-block key that matches no metric, a repeated vendor block and a duplicate metric name are each named in a warning.
911
+
912
+ Dataset-scoped metrics (apache/ossie#591) are named `dataset.metric`, the form the spec references them by, which also keeps them apart from a model-scoped metric of the same bare name. A vendor-block key written in the bare form is named in a warning that suggests the scoped one, since moving a metric onto its dataset renames it. Neither scope gets a `source_model` from Ossie: without a `from_sql`, a `source_model` says the expression reads that table alone, and an Ossie expression names datasets and fields, which may be renamed or computed. Expressions are passed through unchanged; translating them to physical SQL is not done here.
913
+
914
+ *Fields map to physical columns.* A field is a name over an expression, so the table schema registered for a dataset holds the columns those expressions read: a field `customer_name` over `name` registers `name`, and a computed field registers no column. Relationship endpoints are mapped the same way, and a relationship joining on a computed field is skipped with a warning. The column is read with sqlglot, plain SQL first and then BigQuery and T-SQL for backtick and bracket quoting; whichever first reads the expression as a column names it, so `current_date`, `TRUE` and `NULL` register none, and a tokenizer error means no column rather than a failed load. Metric expressions still name fields, and translating them would mean generating SQL, which a consumer whose dialect sqlglot cannot emit (Denodo's VQL) cannot use. So the field vocabulary is carried to where the agent reads the table instead: a column named apart from its field ends its description `(Ossie field: customer_name)`, and the table's description lists each computed field as `name = expression`. Before v0.63.0 the field name was registered as the column, which no test caught because every fixture named its fields after their columns; NVIDIA Auto-Ontology's export does not.
909
915
 
910
916
  **Metric-first domain membership (v0.26.0+):** A metric declares the domains it belongs to via `MetricDefinition.domains`, populated from `meta.domains` by every adapter (`YamlSource`, `DbtSource`, `CubeSource`). The contract's `Domain` model carries only catalog metadata (summary, description, owners, `last_reviewed`) and does **not** list its metrics; it sets `extra="forbid"`, so a stale `metrics:` key from a pre-0.26 contract fails loudly at load time instead of being silently dropped. To enumerate a domain's members, callers reverse-look-up with the `metrics_in_domain(metrics, domain_name)` helper in `base.py` (used by `lookup_domain` and the `list_metrics(domain=...)` filter); per-domain `metric_count` in the prompt index and the `lookup_domain` fuzzy fallback is tallied in a single `Counter` pass over the metrics. This makes the metric the single source of truth for membership: the contract and the semantic source cannot disagree, because only one of them states it. (Prior to v0.26.0 the contract also carried a `Domain.metrics` list, reconciled with `metric.domains` by a union shim; that redundancy — and the drift it allowed — has been removed.)
911
917