terrine 0.1.0.dev0__tar.gz → 0.1.0.dev1__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 (522) hide show
  1. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/ci.yml +4 -0
  2. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.gitignore +1 -0
  3. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/AGENTS.md +58 -9
  4. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/CHANGELOG.md +195 -1
  5. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/Makefile +3 -1
  6. terrine-0.1.0.dev1/PKG-INFO +201 -0
  7. terrine-0.1.0.dev1/README.md +144 -0
  8. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/backend-compatibility.md +264 -18
  9. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/candidate-anchors.md +37 -0
  10. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/capabilities.md +21 -15
  11. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/common-stack-interop.md +20 -6
  12. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/design-specs.md +9 -0
  13. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/first-dataset.md +37 -20
  14. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/public-api.md +33 -0
  15. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/trajectories-survival.md +34 -0
  16. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/reference/refusals.md +5 -3
  17. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/first_dataset.py +1 -1
  18. terrine-0.1.0.dev1/examples/unverified_backend.py +150 -0
  19. terrine-0.1.0.dev1/integration/backends/test_engine_registry.py +25 -0
  20. terrine-0.1.0.dev1/integration/backends/test_generic.py +805 -0
  21. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_time_to_event.py +40 -0
  22. terrine-0.1.0.dev1/integration/conformance/test_engines.py +341 -0
  23. terrine-0.1.0.dev1/integration/conftest.py +24 -0
  24. terrine-0.1.0.dev1/integration/execution/test_generic_provider.py +439 -0
  25. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/conftest.py +3 -0
  26. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_backend_capabilities.py +6 -0
  27. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_ml_staging_examples.py +15 -17
  28. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_admission.py +3 -2
  29. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_execution.py +3 -0
  30. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_shapes.py +19 -0
  31. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_windows.py +0 -17
  32. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/pyproject.toml +4 -1
  33. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tach.toml +5 -5
  34. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/__init__.py +3 -0
  35. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/adapters/increment.py +10 -5
  36. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/design_schema.py +2 -1
  37. terrine-0.1.0.dev1/terrine/backends/capabilities.py +64 -0
  38. terrine-0.1.0.dev1/terrine/backends/dialect.py +716 -0
  39. terrine-0.1.0.dev1/terrine/backends/engines/bigquery.py +192 -0
  40. terrine-0.1.0.dev1/terrine/backends/engines/duckdb.py +301 -0
  41. terrine-0.1.0.dev1/terrine/backends/engines/postgres.py +355 -0
  42. terrine-0.1.0.dev1/terrine/backends/engines/snowflake.py +277 -0
  43. terrine-0.1.0.dev1/terrine/backends/extrema.py +25 -0
  44. terrine-0.1.0.dev1/terrine/backends/joins.py +41 -0
  45. terrine-0.1.0.dev1/terrine/backends/numeric.py +476 -0
  46. terrine-0.1.0.dev1/terrine/backends/registry.py +284 -0
  47. terrine-0.1.0.dev1/terrine/backends/sequences.py +14 -0
  48. terrine-0.1.0.dev1/terrine/backends/timestamps.py +234 -0
  49. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/_asof.py +5 -1
  50. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/anchors.py +18 -9
  51. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/columns.py +29 -5
  52. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/cost.py +8 -1
  53. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/dispatch.py +10 -4
  54. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/layers.py +1 -0
  55. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/materialize.py +1 -1
  56. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/_observations.py +3 -0
  57. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/cohorts.py +30 -12
  58. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/event_sequence.py +3 -0
  59. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/ltv.py +34 -8
  60. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/matched_t0.py +3 -0
  61. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/time_to_event.py +4 -6
  62. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/data.py +80 -2
  63. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/reference.py +4 -5
  64. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/errors.py +5 -1
  65. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_bigquery.py +81 -14
  66. terrine-0.1.0.dev1/terrine/execution/_generic.py +385 -0
  67. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_native.py +57 -8
  68. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_postgres.py +90 -82
  69. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_publish.py +1 -1
  70. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_snowflake.py +93 -23
  71. terrine-0.1.0.dev1/terrine/execution/backend.py +100 -0
  72. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/inputs.py +62 -19
  73. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/lifelines.py +1 -0
  74. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/preflight.py +2 -1
  75. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/frame.py +19 -4
  76. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/model.py +82 -11
  77. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/api.py +4 -0
  78. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/backends/duckdb.py +2 -2
  79. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/planning.py +1 -1
  80. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/snapshot.py +1 -1
  81. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/bind.py +26 -12
  82. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/io.py +1 -0
  83. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/rules.py +2 -1
  84. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/sql.py +90 -26
  85. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/anchors.py +24 -9
  86. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/io.py +4 -2
  87. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/validate.py +9 -4
  88. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/aggregate.py +43 -46
  89. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/attributes.py +65 -45
  90. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/daily.py +23 -34
  91. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/first.py +6 -28
  92. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/prefix.py +42 -8
  93. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/.test_durations +0 -3
  94. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_schemas.py +1 -0
  95. terrine-0.1.0.dev1/tests/backends/test_engine_registry.py +225 -0
  96. terrine-0.1.0.dev1/tests/backends/test_generic.py +457 -0
  97. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/backends/test_numeric.py +5 -5
  98. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/backends/test_sequences.py +1 -1
  99. terrine-0.1.0.dev1/tests/backends/test_timestamps.py +120 -0
  100. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_anchors.py +34 -2
  101. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_cohorts.py +17 -0
  102. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_columns.py +58 -0
  103. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_event_sequence.py +0 -16
  104. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_ltv.py +63 -0
  105. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_persisted_layers.py +25 -10
  106. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_training_view.py +3 -0
  107. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_winsorization_definition.py +3 -0
  108. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/documentation.py +5 -1
  109. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/execution/test_backend_provider.py +129 -27
  110. terrine-0.1.0.dev1/tests/execution/test_generic.py +311 -0
  111. terrine-0.1.0.dev1/tests/execution/test_sql_verification.py +106 -0
  112. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/conftest.py +3 -0
  113. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_warehouse_preflight.py +21 -0
  114. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/baseline.py +3 -0
  115. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/test_baseline.py +3 -0
  116. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_backend_capabilities.py +2 -1
  117. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_layers.py +5 -1
  118. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_planning.py +6 -3
  119. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/semantic/test_metric_rules.py +17 -1
  120. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/semantic/test_rules_import.py +5 -1
  121. terrine-0.1.0.dev1/tests/semantic/test_sql.py +94 -0
  122. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_design_io.py +41 -0
  123. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_nonadditive_spec.py +31 -1
  124. terrine-0.1.0.dev1/tests/temporal/__init__.py +0 -0
  125. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_first_occurrence.py +21 -0
  126. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_scd2_attributes.py +19 -22
  127. terrine-0.1.0.dev1/tests/test_engine_dispatch.py +401 -0
  128. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_frame_runtime.py +10 -0
  129. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_metadata.py +203 -32
  130. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_optional_dependencies.py +1 -0
  131. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/uv.lock +1 -1
  132. terrine-0.1.0.dev0/PKG-INFO +0 -164
  133. terrine-0.1.0.dev0/README.md +0 -107
  134. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-build.md +0 -416
  135. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-dataset-workflows.md +0 -164
  136. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-study-api.md +0 -312
  137. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-temporal.md +0 -345
  138. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-17-terrine-frame.md +0 -182
  139. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-event-sequences.md +0 -200
  140. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-materialization-runtime.md +0 -267
  141. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-ml-staging.md +0 -201
  142. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-orchestrators.md +0 -131
  143. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-sequential-panels.md +0 -183
  144. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-trajectories-survival.md +0 -269
  145. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-warehouse-scale.md +0 -163
  146. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-agent-tools.md +0 -181
  147. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-fast-tests-inventory.md +0 -478
  148. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-fast-tests-ledger.md +0 -561
  149. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-fast-tests.md +0 -145
  150. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-interop.md +0 -202
  151. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-library-handoffs.md +0 -81
  152. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-release-readiness.md +0 -100
  153. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-20-terrine-test-tiers.md +0 -113
  154. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-21-terrine-postgres-execution.md +0 -170
  155. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-22-bigquery-execution.md +0 -66
  156. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-22-ibis-study-queries.md +0 -73
  157. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-22-postgres-float-windows.md +0 -56
  158. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-23-exact-float-windows.md +0 -88
  159. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-23-snowflake-execution.md +0 -323
  160. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-24-mutation-audit.md +0 -80
  161. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-24-test-suite-speed.md +0 -68
  162. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-26-public-readiness.md +0 -255
  163. terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-26-table-first-binding-and-recipes.md +0 -184
  164. terrine-0.1.0.dev0/docs/superpowers/plans/ledger-compile.md +0 -345
  165. terrine-0.1.0.dev0/docs/superpowers/plans/ledger-dataset.md +0 -136
  166. terrine-0.1.0.dev0/docs/superpowers/plans/ledger-runtime.md +0 -180
  167. terrine-0.1.0.dev0/docs/superpowers/plans/ledger-test-tiers-misc.md +0 -14
  168. terrine-0.1.0.dev0/docs/superpowers/plans/ledger-test-tiers-state-panels.md +0 -37
  169. terrine-0.1.0.dev0/docs/superpowers/plans/ledger-test-tiers-temporal.md +0 -29
  170. terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-16-terrine-handoff.md +0 -635
  171. terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-17-terrine-frame-design.md +0 -135
  172. terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-18-terrine-scope-datasets-only.md +0 -76
  173. terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-26-table-first-binding-and-recipes-design.md +0 -146
  174. terrine-0.1.0.dev0/integration/conftest.py +0 -1
  175. terrine-0.1.0.dev0/terrine/backends/capabilities.py +0 -117
  176. terrine-0.1.0.dev0/terrine/backends/dialect.py +0 -55
  177. terrine-0.1.0.dev0/terrine/backends/extrema.py +0 -40
  178. terrine-0.1.0.dev0/terrine/backends/joins.py +0 -75
  179. terrine-0.1.0.dev0/terrine/backends/numeric.py +0 -657
  180. terrine-0.1.0.dev0/terrine/backends/sequences.py +0 -40
  181. terrine-0.1.0.dev0/terrine/backends/timestamps.py +0 -421
  182. terrine-0.1.0.dev0/terrine/execution/backend.py +0 -349
  183. terrine-0.1.0.dev0/tests/backends/test_timestamps.py +0 -62
  184. terrine-0.1.0.dev0/tests/semantic/test_sql.py +0 -32
  185. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/CLA.md +0 -0
  186. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/cla.yml +0 -0
  187. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/release.yml +0 -0
  188. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/scale.yml +0 -0
  189. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/warehouse-backends.yml +0 -0
  190. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.kata.toml +0 -0
  191. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.pre-commit-config.yaml +0 -0
  192. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.python-version +0 -0
  193. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.roborev.toml +0 -0
  194. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/CLAUDE.md +0 -0
  195. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/CONTRIBUTING.md +0 -0
  196. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/LICENSE +0 -0
  197. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/NOTICE +0 -0
  198. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/THIRD_PARTY_NOTICES.txt +0 -0
  199. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/README.md +0 -0
  200. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/report.py +0 -0
  201. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/results/duckdb-scale-v2-small.json +0 -0
  202. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/run_duckdb.py +0 -0
  203. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/run_live.py +0 -0
  204. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/run_overhead.py +0 -0
  205. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/workloads/small.json +0 -0
  206. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/workloads.py +0 -0
  207. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/common-feature-patterns.md +0 -0
  208. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/cost-and-residency.md +0 -0
  209. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/dataset-agent-tools.md +0 -0
  210. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/design-validation.md +0 -0
  211. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/increment-compatibility.md +0 -0
  212. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/index.md +0 -0
  213. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/library-handoffs.md +0 -0
  214. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/limitations.md +0 -0
  215. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/materialization-runtime.md +0 -0
  216. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/ml-staging.md +0 -0
  217. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/orchestrators.md +0 -0
  218. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/semantic-layer-interop.md +0 -0
  219. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/sequential-panels.md +0 -0
  220. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/shared-dataset-contract.md +0 -0
  221. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/state-panels.md +0 -0
  222. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/synthetic-worlds.md +0 -0
  223. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/temporal-attributes.md +0 -0
  224. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/temporal-engine.md +0 -0
  225. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/index.md +0 -0
  226. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/edge-case-designs.md +0 -0
  227. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/landscape.md +0 -0
  228. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/ohdsi-study-design.md +0 -0
  229. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/point-in-time-feature-apis.md +0 -0
  230. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/evals/agent_dataset_tools.yaml +0 -0
  231. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/batch_scoring.py +0 -0
  232. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/bigquery/dataset.py +0 -0
  233. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/common_stack_interop.py +0 -0
  234. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/dataset_agent_tools.py +0 -0
  235. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/event_sequences.py +0 -0
  236. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/increment_parity.py +0 -0
  237. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/materialization_runtime.py +0 -0
  238. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/ml_staging.py +0 -0
  239. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/nonadditive_metrics.py +0 -0
  240. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/orchestrators/airflow_materialization.py +0 -0
  241. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/orchestrators/dagster_materialization.py +0 -0
  242. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/orchestrators/dbt_core_after_build.py +0 -0
  243. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/postgres/dataset.py +0 -0
  244. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/retention_and_winsorization.py +0 -0
  245. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/retention_metrics.py +0 -0
  246. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/sequential_trials.py +0 -0
  247. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/snowflake/dataset.py +0 -0
  248. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/state_panel.py +0 -0
  249. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/table_first_dataset.py +0 -0
  250. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/trajectories_survival.py +0 -0
  251. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/.test_durations +0 -0
  252. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/agent/test_dataset_tool_example.py +0 -0
  253. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/agent/test_dataset_tool_flow.py +0 -0
  254. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/agent/test_dataset_tool_runtime.py +0 -0
  255. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/backends/test_numeric.py +0 -0
  256. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compat/test_native_catalog.py +0 -0
  257. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_boolean_outputs.py +0 -0
  258. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_column_runtime_contracts.py +0 -0
  259. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_frame_runtime_window_export.py +0 -0
  260. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_ltv.py +0 -0
  261. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_retention.py +0 -0
  262. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/__init__.py +0 -0
  263. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/test_diff_diff.py +0 -0
  264. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/test_library_handoffs.py +0 -0
  265. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/test_lifelines.py +0 -0
  266. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/parity/test_increment_panel_parity.py +0 -0
  267. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/state_panels/test_state_panel_workflows.py +0 -0
  268. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/temporal/test_retention_bands.py +0 -0
  269. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/temporal/test_window_quotient_composition.py +0 -0
  270. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/test_independence.py +0 -0
  271. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/test_model_from_tables.py +0 -0
  272. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/bigquery_fixtures.py +0 -0
  273. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/postgres_fixtures.py +0 -0
  274. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/snowflake_fixtures.py +0 -0
  275. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_bigquery_admission.py +0 -0
  276. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_bigquery_execution.py +0 -0
  277. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_bigquery_parity.py +0 -0
  278. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_duckdb_compiler_scale.py +0 -0
  279. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_duckdb_runtime_recovery.py +0 -0
  280. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_duckdb_scale.py +0 -0
  281. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_incremental_equivalence.py +0 -0
  282. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_live_warehouse_scale.py +0 -0
  283. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_materialization_example.py +0 -0
  284. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_ml_staging_duckdb.py +0 -0
  285. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_orchestrator_adapters.py +0 -0
  286. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_planning.py +0 -0
  287. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_timestamps.py +0 -0
  288. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_request_replay_process.py +0 -0
  289. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_api.py +0 -0
  290. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_cli.py +0 -0
  291. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_control.py +0 -0
  292. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_cost_budget.py +0 -0
  293. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_saved_plan_restart.py +0 -0
  294. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snapshot_capture_bounds.py +0 -0
  295. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snapshot_partition_consistency.py +0 -0
  296. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snowflake_admission.py +0 -0
  297. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snowflake_execution.py +0 -0
  298. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snowflake_parity.py +0 -0
  299. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_winsorization_definition.py +0 -0
  300. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/mkdocs.yml +0 -0
  301. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/scripts/mutate.py +0 -0
  302. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/_time.py +0 -0
  303. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/adapters/__init__.py +0 -0
  304. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/adapters/opentelemetry.py +0 -0
  305. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/__init__.py +0 -0
  306. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/definitions.py +0 -0
  307. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/policy.py +0 -0
  308. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/schemas.py +0 -0
  309. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/tools.py +0 -0
  310. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/backends/__init__.py +0 -0
  311. {terrine-0.1.0.dev0/tests → terrine-0.1.0.dev1/terrine/backends/engines}/__init__.py +0 -0
  312. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/__init__.py +0 -0
  313. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/_predicates.py +0 -0
  314. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/provenance.py +0 -0
  315. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/ranges.py +0 -0
  316. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/retention.py +0 -0
  317. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/__init__.py +0 -0
  318. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/calendar_comparison.py +0 -0
  319. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/event_panel.py +0 -0
  320. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/life_table.py +0 -0
  321. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/sequential.py +0 -0
  322. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/state_panel.py +0 -0
  323. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/__init__.py +0 -0
  324. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/export.py +0 -0
  325. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/manifest.py +0 -0
  326. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/splits.py +0 -0
  327. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/training.py +0 -0
  328. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/transforms.py +0 -0
  329. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/__init__.py +0 -0
  330. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_metadata.py +0 -0
  331. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/gates.py +0 -0
  332. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/__init__.py +0 -0
  333. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/diff_diff.py +0 -0
  334. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/surrogate.py +0 -0
  335. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/orchestration/__init__.py +0 -0
  336. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/orchestration/airflow.py +0 -0
  337. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/orchestration/dagster.py +0 -0
  338. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/py.typed +0 -0
  339. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/recipes.py +0 -0
  340. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/__init__.py +0 -0
  341. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/arrow.py +0 -0
  342. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/backends/base.py +0 -0
  343. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/changes.py +0 -0
  344. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/cli.py +0 -0
  345. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/control.py +0 -0
  346. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/cost.py +0 -0
  347. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/events.py +0 -0
  348. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/execute.py +0 -0
  349. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/gc.py +0 -0
  350. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/graph.py +0 -0
  351. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/layers.py +0 -0
  352. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/leases.py +0 -0
  353. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/materialize.py +0 -0
  354. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/owner.py +0 -0
  355. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/planner.py +0 -0
  356. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/saved_plan.py +0 -0
  357. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/state.py +0 -0
  358. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/status.py +0 -0
  359. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/synchronization.py +0 -0
  360. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/__init__.py +0 -0
  361. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/models.py +0 -0
  362. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/time.py +0 -0
  363. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/__init__.py +0 -0
  364. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/baseline.yaml +0 -0
  365. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/generate.py +0 -0
  366. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/scale.py +0 -0
  367. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/world.py +0 -0
  368. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/__init__.py +0 -0
  369. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/dataset.py +0 -0
  370. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/design.py +0 -0
  371. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/expressions.py +0 -0
  372. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/panels.py +0 -0
  373. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/sequence.py +0 -0
  374. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/state_panel.py +0 -0
  375. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/trajectory.py +0 -0
  376. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/transforms.py +0 -0
  377. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/__init__.py +0 -0
  378. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/lookup.py +0 -0
  379. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/nonadditive.py +0 -0
  380. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/ranges.py +0 -0
  381. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/readiness.py +0 -0
  382. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/retention.py +0 -0
  383. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/sequence.py +0 -0
  384. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/sessionize.py +0 -0
  385. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/sketches.py +0 -0
  386. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/windows.py +0 -0
  387. {terrine-0.1.0.dev0/tests/backends → terrine-0.1.0.dev1/tests}/__init__.py +0 -0
  388. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/_timing.py +0 -0
  389. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/_worlds.py +0 -0
  390. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/adapters/test_airflow.py +0 -0
  391. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/adapters/test_dagster.py +0 -0
  392. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/adapters/test_dbt_core_after_build.py +0 -0
  393. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/conftest.py +0 -0
  394. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/fixtures/catalog.yaml +0 -0
  395. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_inspect_design.py +0 -0
  396. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_materialize.py +0 -0
  397. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_plan_authorization.py +0 -0
  398. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_policy.py +0 -0
  399. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_requests.py +0 -0
  400. {terrine-0.1.0.dev0/tests/compat → terrine-0.1.0.dev1/tests/backends}/__init__.py +0 -0
  401. {terrine-0.1.0.dev0/tests/compile → terrine-0.1.0.dev1/tests/compat}/__init__.py +0 -0
  402. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/catalog.yaml +0 -0
  403. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/increment.yaml +0 -0
  404. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/same-name-collision.yaml +0 -0
  405. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/unsupported-metric.yaml +0 -0
  406. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/test_increment_semantics.py +0 -0
  407. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/test_semantic_interop.py +0 -0
  408. {terrine-0.1.0.dev0/tests/dataset → terrine-0.1.0.dev1/tests/compile}/__init__.py +0 -0
  409. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_completeness_columns.py +0 -0
  410. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_cost_estimates.py +0 -0
  411. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_matched_t0.py +0 -0
  412. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_nonadditive.py +0 -0
  413. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_panels.py +0 -0
  414. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_range_scaling.py +0 -0
  415. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_retention.py +0 -0
  416. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_state_panel.py +0 -0
  417. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_survival_bounds.py +0 -0
  418. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_time_to_event.py +0 -0
  419. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_winsorization.py +0 -0
  420. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/conftest.py +0 -0
  421. {terrine-0.1.0.dev0/tests/execution → terrine-0.1.0.dev1/tests/dataset}/__init__.py +0 -0
  422. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_definition.py +0 -0
  423. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_label_support.py +0 -0
  424. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_manifest.py +0 -0
  425. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_runtime_staging.py +0 -0
  426. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_splits.py +0 -0
  427. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_staging.py +0 -0
  428. {terrine-0.1.0.dev0/tests/fixtures → terrine-0.1.0.dev1/tests/execution}/__init__.py +0 -0
  429. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/execution/test_inputs.py +0 -0
  430. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/execution/test_snowflake_session.py +0 -0
  431. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_diff_diff.py +0 -0
  432. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_lifelines.py +0 -0
  433. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_preflight.py +0 -0
  434. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_surrogate.py +0 -0
  435. {terrine-0.1.0.dev0/tests/integration → terrine-0.1.0.dev1/tests/fixtures}/__init__.py +0 -0
  436. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/interop.py +0 -0
  437. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/materialized_frame_v1.json +0 -0
  438. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_definition.json +0 -0
  439. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_fitted_definition.json +0 -0
  440. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_fitted_transform.json +0 -0
  441. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_invocation.json +0 -0
  442. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_manifest.json +0 -0
  443. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_plan_request.json +0 -0
  444. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_saved_plan.json +0 -0
  445. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/winsorization.py +0 -0
  446. {terrine-0.1.0.dev0/tests/parity → terrine-0.1.0.dev1/tests/integration}/__init__.py +0 -0
  447. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/distribution_smoke.py +0 -0
  448. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/postgres_scratch.py +0 -0
  449. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/study_api_smoke.py +0 -0
  450. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/test_benchmark_failure_reporting.py +0 -0
  451. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/test_live_scale_gate.py +0 -0
  452. {terrine-0.1.0.dev0/tests/reference → terrine-0.1.0.dev1/tests/parity}/__init__.py +0 -0
  453. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/parity/test_increment_frames.py +0 -0
  454. {terrine-0.1.0.dev0/tests/runtime → terrine-0.1.0.dev1/tests/reference}/__init__.py +0 -0
  455. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/baseline.py +0 -0
  456. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/nonadditive.py +0 -0
  457. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/panels.py +0 -0
  458. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/ranges.py +0 -0
  459. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/retention.py +0 -0
  460. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/scale_metrics.py +0 -0
  461. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/test_baseline.py +0 -0
  462. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/test_event_sequences.py +0 -0
  463. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/test_panels.py +0 -0
  464. {terrine-0.1.0.dev0/tests/semantic → terrine-0.1.0.dev1/tests/runtime}/__init__.py +0 -0
  465. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_api.py +0 -0
  466. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_arrow_output.py +0 -0
  467. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_batch_cleanup.py +0 -0
  468. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_cancel_publication.py +0 -0
  469. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_cli.py +0 -0
  470. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_control_ipc.py +0 -0
  471. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_control_recovery.py +0 -0
  472. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_cost_estimates.py +0 -0
  473. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_duckdb_publication.py +0 -0
  474. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_execute.py +0 -0
  475. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_gc.py +0 -0
  476. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_graph.py +0 -0
  477. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_imports.py +0 -0
  478. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_invalidation.py +0 -0
  479. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_leases.py +0 -0
  480. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_opentelemetry.py +0 -0
  481. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_planner.py +0 -0
  482. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_recovery_lease.py +0 -0
  483. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_request_replay.py +0 -0
  484. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_run_events.py +0 -0
  485. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_run_status.py +0 -0
  486. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_saved_plan.py +0 -0
  487. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_snapshot.py +0 -0
  488. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_snapshot_lifecycle.py +0 -0
  489. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_state.py +0 -0
  490. {terrine-0.1.0.dev0/tests/simulate → terrine-0.1.0.dev1/tests/semantic}/__init__.py +0 -0
  491. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/semantic/test_catalog.py +0 -0
  492. {terrine-0.1.0.dev0/tests/spec → terrine-0.1.0.dev1/tests/simulate}/__init__.py +0 -0
  493. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/simulate/test_scale_generator.py +0 -0
  494. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/simulate/test_world.py +0 -0
  495. {terrine-0.1.0.dev0/tests/temporal → terrine-0.1.0.dev1/tests/spec}/__init__.py +0 -0
  496. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_calendar_comparison.py +0 -0
  497. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_expressions.py +0 -0
  498. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_panels.py +0 -0
  499. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_recipes.py +0 -0
  500. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_retention.py +0 -0
  501. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_sequence.py +0 -0
  502. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_sketch_refusal.py +0 -0
  503. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_state_panel.py +0 -0
  504. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_validate.py +0 -0
  505. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_nonadditive.py +0 -0
  506. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_prefix_lookup.py +0 -0
  507. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_range_scaling.py +0 -0
  508. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_readiness.py +0 -0
  509. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_retention.py +0 -0
  510. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_semantic_binding.py +0 -0
  511. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sequence.py +0 -0
  512. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sessionize.py +0 -0
  513. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sketches.py +0 -0
  514. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sparse_state.py +0 -0
  515. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_window_aggregates.py +0 -0
  516. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_common_stack_interop.py +0 -0
  517. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_distribution.py +0 -0
  518. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_documentation.py +0 -0
  519. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_errors.py +0 -0
  520. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_frame.py +0 -0
  521. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_mutation_runner.py +0 -0
  522. {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_windows.py +0 -0
@@ -72,6 +72,10 @@ jobs:
72
72
  --splits 6 --group ${{ matrix.group }}
73
73
  --splitting-algorithm least_duration
74
74
  --durations-path integration/.test_durations
75
+ # The same known answers compiled through the portable base expression
76
+ # policy; about twenty seconds beside the native run.
77
+ - if: matrix.group == 1
78
+ run: uv run pytest -q integration/conformance --generic-dialect
75
79
  docs-and-examples:
76
80
  runs-on: ubuntu-latest
77
81
  timeout-minutes: 30
@@ -14,6 +14,7 @@ dist/
14
14
  build/
15
15
  *.egg-info/
16
16
  .superpowers/
17
+ docs/superpowers/
17
18
  .worktrees/
18
19
 
19
20
  # Local mutation campaigns
@@ -94,12 +94,30 @@ Reached schedules and deadlines use notify.* for the current owner, or the autho
94
94
  random sample without replacement; the default executes every mutant.
95
95
  Campaigns are local only, not part of ordinary checks or CI.
96
96
  - `make test-postgres` runs the live PostgreSQL probes (`@pytest.mark.postgres`,
97
- deselected by default); they fail, never skip, without
97
+ deselected by default) and then the conformance known answers with the
98
+ portable base expression policy forced; they fail, never skip, without
98
99
  `TERRINE_TEST_POSTGRES_*` settings. Start a throwaway local server with
99
100
  `eval "$(uv run python -m tests.integration.postgres_scratch start)"` and stop it
100
101
  with `uv run python -m tests.integration.postgres_scratch stop`. CI runs them
101
102
  on every push against a PostgreSQL 16 service container. Each probe owns a
102
103
  uniquely named schema; never share a warehouse namespace between tests.
104
+ - `uv run pytest integration/conformance` runs known answers on DuckDB by default,
105
+ with parameters derived from the verified engine registry. Select the same cases
106
+ on a configured scratch warehouse with
107
+ `uv run --extra <engine> pytest integration/conformance -m <engine>` for
108
+ `postgres`, `bigquery` or `snowflake`; they are also included in the matching
109
+ `make test-<engine>` target. The module's connection-fixture table must cover
110
+ every registered engine. Add `--generic-dialect` to compile the same known
111
+ answers with the portable base expression policy on each real connection
112
+ (`uv run pytest integration/conformance --generic-dialect`; with
113
+ `--extra postgres ... -m postgres` on the scratch server). The forced run
114
+ takes about twenty seconds per backend and is part of the integration and
115
+ PostgreSQL checks on every push; the portable policy's own numerical and
116
+ relational oracles live in `integration/backends/test_generic.py` and force
117
+ it unconditionally, and its real unregistered-backend reads on Ibis SQLite
118
+ live in `integration/execution/test_generic_provider.py`. Test module
119
+ basenames under `integration/` must be unique: the tree has no packages, so
120
+ two `test_x.py` files cannot both be collected.
103
121
  - `make test-bigquery` runs the live BigQuery probes (`@pytest.mark.bigquery`,
104
122
  deselected by default); they fail, never skip, without
105
123
  `TERRINE_TEST_BIGQUERY_PROJECT`, `TERRINE_TEST_BIGQUERY_DATASET` and
@@ -242,16 +260,47 @@ Reached schedules and deadlines use notify.* for the current owner, or the autho
242
260
  raw SQL strings or `@ibis.udf.*.builtin` bindings to native functions there;
243
261
  ruff (`TID251`) rejects `ibis.udf` and `ibis.to_sql` outside the allowed
244
262
  packages.
245
- - When Ibis lacks or mistranslates an operation on a supported backend, add a
246
- dispatched helper in `terrine/backends/` (`dialect`, `numeric`, `timestamps`,
247
- `joins`, `extrema`) with a live parity test, not a workaround at each call
248
- site. Supporting a new warehouse means implementing those helpers for it.
249
- Known Ibis gaps: no ASOF JOIN on BigQuery, NULL-propagating LEAST/GREATEST
250
- there, and `interval * column` lowered through `EXTRACT(SECOND ...)` (multiply
251
- integer seconds, then `.as_interval("s")`).
263
+ - When Ibis lacks or mistranslates an operation on a supported backend, implement
264
+ the override in that engine's `terrine/backends/engines/<name>.py` dialect,
265
+ with a live parity test, not a workaround at each call site. Shared helpers
266
+ (`numeric`, `timestamps`, `joins`, `extrema`, `sequences`) delegate engine
267
+ differences to the selected dialect. Native function bindings belong in
268
+ concrete engine modules. Known Ibis gaps: no ASOF JOIN on BigQuery,
269
+ NULL-propagating LEAST/GREATEST there, and `interval * column` lowered through
270
+ `EXTRACT(SECOND ...)` (multiply integer seconds, then `.as_interval("s")`).
252
271
  - Raw SQL is acceptable only for: terrine's own DuckDB runtime state tables
253
272
  (`terrine/runtime/`), backend-provider catalog introspection and transaction
254
- control (`terrine/execution/`), and admitted user SQL relations.
273
+ control (`terrine/execution/`), admitted user SQL relations, and the existing
274
+ qualified read-only PostgreSQL identity callback in `terrine/backends/engines/`.
275
+ - `terrine/backends/registry.py` owns verified engine names, capability values,
276
+ feature boundaries and lazy dialect, identity and execution-provider paths.
277
+ Add an engine through that registry and one concrete dialect override module,
278
+ with admission evidence; do not introduce supported-engine lists in consumers.
279
+ Capability inspection is metadata-only and never runs querying identity admission.
280
+ `tests/test_engine_dispatch.py` rejects engine-name selection outside those
281
+ modules; it is a heuristic review aid, not proof of centralized dispatch.
282
+ - Backends have three tiers. Verified engines are the `ENGINES` registrations
283
+ with native dialect overrides and consistent-view providers. Any other
284
+ genuine Ibis backend is unverified: it refuses by default and, only after a
285
+ Model's `allow_unverified=True`, runs read-only statement-scoped reads under
286
+ the separate `GENERIC` record, whose expression policy is the unmodified
287
+ base `Dialect` in `terrine/backends/dialect.py` and whose provider is
288
+ `terrine/execution/_generic.py`. Raw drivers, name-only objects and failed
289
+ registered identities stay refused with or without the flag, as do every
290
+ effect (staging, retained capture, publication, reopening) on an unverified
291
+ backend. Extend the portable tier by completing the base `Dialect` method
292
+ or adding an operation-specific coded refusal there, never by naming a
293
+ backend; a base default must not advertise an operation a registered
294
+ engine's `operations` declaration excludes, so add the engine override that
295
+ keeps its refusal. The portable policy answers exactly or refuses by code:
296
+ lazy scalar known-answer witnesses (exact decimals, UTC wall clocks across
297
+ daylight saving) run once per execution context before source rows are
298
+ read, and accumulations are withheld by a derived magnitude bound rather
299
+ than trusted to the backend.
300
+ - The PostgreSQL engine's identity callback owns the existing qualified,
301
+ read-only `SELECT pg_catalog.version()` admission exception. It checks the
302
+ Ibis floor before reading identity and refuses before source metadata access;
303
+ expression policies otherwise issue no connection probes.
255
304
  - Warehouse results match the DuckDB reference within a stated tolerance
256
305
  (floats: tight, relative to the window's absolute sum, since cancellation
257
306
  defeats a purely relative bound; exact for counts and decimal sums).
@@ -1,6 +1,200 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.1.0.dev1
4
+
5
+ ### Backend compatibility
6
+
7
+ - Admit any other genuine Ibis backend for read-only use only after an
8
+ explicit `allow_unverified=True` on `Model.from_catalog`, `Model.from_tables`
9
+ or `Model.from_increment`. By default such a backend refuses with
10
+ `terrine.backend.capability_unavailable` (`operation="query"`,
11
+ `status="unverified"`, a route naming the flag and the verified engines)
12
+ before its schema is read; a non-Boolean choice refuses with
13
+ `terrine.model.invalid_binding`. Admission emits the new filterable
14
+ `terrine.UnverifiedBackendWarning` once at the caller's line and binds
15
+ consent to that Model, not the connection. Registered identity failures,
16
+ raw drivers and name-only connections remain refused with or without the
17
+ flag. The separate `GENERIC` engine record is read-only: retained input
18
+ capture, staging, publication, published Dataset reopening, native Parquet
19
+ unload and transaction snapshots refuse before any write, and strict scan
20
+ budgets still refuse unknown source costs. `Catalog.dialect` keeps the
21
+ connection's real SQL dialect; the expression policy is `generic`.
22
+ - Execute admitted reads on the caller's borrowed connection, one independent
23
+ statement at a time: Terrine issues no transaction control, session
24
+ settings, temporary objects, cloning or disconnection, but Ibis schema
25
+ inspection may end an open caller transaction, so pass a connection with no
26
+ pending writes or a dedicated read connection. The execution snapshot has
27
+ no identity and `replay_equivalence="unverified"`. Admitted SQL and every
28
+ compiled statement are parsed for read-only safety in the real dialect and
29
+ fail closed with `terrine.sql.invalid_read_query`. Every SQL verification
30
+ step on every engine (parsing, rendering, scope analysis, the PostgreSQL
31
+ function check, the BigQuery and Snowflake routine checks and Snowflake's
32
+ Time Travel pinning) runs on a fresh thread, whose recursion depth is
33
+ counted separately, so a deeply nested compiled statement is verified
34
+ whatever the caller's own stack depth without changing the interpreter's
35
+ recursion limit, and relation discovery reads the verified scope graph
36
+ rather than traversing nested derived tables again; a statement too deep to
37
+ verify even then refuses with `terrine.sql.invalid_read_query` and
38
+ `reason="too_deep_to_verify"`, never as unsafe SQL or a raw
39
+ `RecursionError`. Unsupported primitives
40
+ refuse with `capability_unavailable` naming the backend and operation,
41
+ chained from the backend's exception; owned Arrow readers are released on
42
+ exhaustion, error, early close or context closure, and a failure during
43
+ batch consumption carries the same code.
44
+ - Run lazy scalar known-answer witnesses once per execution context, before
45
+ any source row and only when a relation needs them; materialization and the
46
+ bounded preview submit every relation they will run (output, audit and each
47
+ integrity check) as soon as the study is compiled, so an unsupported
48
+ primitive refuses before its roster and event checks scan, while an
49
+ independent statement on an execution context is admitted when issued:
50
+ exact decimal arithmetic
51
+ (scale-9 low digits at a large offset, cancellation, the domain edges,
52
+ signed quotients, both `int64` endpoints and a 38-digit coefficient), which
53
+ refuses `operation="exact_state"` on a backend such as SQLite that stores
54
+ decimals by affinity, and UTC wall clocks of aware-timestamp deltas at four
55
+ instants across daylight-saving, pre-epoch and local-mean-time offsets,
56
+ which refuses `operation="utc_wall_clock"` where Ibis lowers the delta
57
+ through session wall clocks (PostgreSQL-family backends in a non-UTC
58
+ session) rather than answering by the session offset. Nanosecond
59
+ timestamps refuse the portable UTC conversions (`utc_wall_clock`,
60
+ `utc_instant`) while the expression is built; sub-second parts keep
61
+ nanosecond support.
62
+ - Complete the portable base expression policy used after unverified
63
+ admission, so every study expression is an ordinary Ibis expression without
64
+ native bindings. Float facts enter as `DECIMAL(38, 9)` state by truncating
65
+ the backend's decimal text toward zero at nine places (`|Q9(t) - t| <
66
+ 10**-9`; magnitudes at or above `10**29` and non-finite values refuse with
67
+ the existing `float64_precision` failures); counts and source-decimal
68
+ coefficients stay in a separate `DECIMAL(38, 0)` domain whose literals,
69
+ typed NULLs and narrowing bounds take the coefficient's type. Prefix
70
+ differences leaving the fixed domain and outputs leaving their target type
71
+ refuse with the existing codes. Accumulations are admitted only when a
72
+ binary64 bound on their magnitudes proves the exact sum fits: with `n`
73
+ contributing values the computed bound `B` lies within
74
+ `e = (1 + u)(1 + γ_{n-1}) - 1` of the true magnitude sum (`u = 2**-53`,
75
+ `γ_k = k·u / (1 - k·u)`), so a partition is admitted iff
76
+ `B < limit · (1 - e)`, evaluated with its own rounding on the conservative
77
+ side; otherwise its values are withheld before the backend adds them and
78
+ every consumer of the state (sums, averages, ratios, cohort totals and
79
+ means, cumulative trajectories) refuses with `float64_precision`
80
+ (`bound_overflow`) for float facts or `exact_overflow` for coefficients,
81
+ never a backend error, a narrowed value or a silent NULL. The bound is
82
+ deliberately conservative: opposite-sign history whose magnitudes alone
83
+ exceed the domain is refused even where the exact difference would fit.
84
+ Exact-state ratios divide correctly rounded binary64 operands once (within
85
+ `3.1u` before later rounding); integer quotients truncate toward zero
86
+ through a certified decimal residual correction and are NULL beyond int64
87
+ or for a zero divisor. Timestamps use native order, typed UTC epoch deltas
88
+ and signed parts; fixed-offset local days and their start instants derive
89
+ from one epoch delta and one floor quotient (`Dialect.local_day`,
90
+ `Dialect.day_start`) with every intermediate inside `int64` at both
91
+ endpoints under the extreme offsets, which keeps day buckets and endpoint
92
+ alignment shallow where chained conversions would nest (registered engines
93
+ keep their cast-based forms, compiled unchanged); the union/window as-of
94
+ fallback, coalesced extrema and portable integer sequences are the
95
+ defaults. Measured on DuckDB and
96
+ PostgreSQL 16 with the policy forced: shortest round-trip float text within
97
+ half an ulp, exact `0.1 + 0.2` and scale-2 cancellation, both signed
98
+ `int64` quotient endpoints, and nested quotients within `8u`. Registered
99
+ engines keep their native state and every previously refused operation
100
+ (`exact_ratio` and `exact_is_nonfinite` on DuckDB and Snowflake; normalized
101
+ significand division on PostgreSQL and BigQuery) through explicit
102
+ overrides; their native conversions, refusals and session lifetimes are
103
+ unchanged.
104
+ - Record the engine stamp on every result: required `Provenance.engine_name`
105
+ (actual backend), `dialect` (admitted expression policy) and `engine_status`
106
+ (its registered status), written by direct materialization, the staged
107
+ runtime and every fixture builder, and retained through views, cohorts,
108
+ life tables, Arrow, JSON, manifests and exports. Arrow metadata stays
109
+ version 2 under `terrine.materialized_frame.v2` and JSON metadata version 1.
110
+ Payloads saved before the stamp existed decode as their recorded verified
111
+ execution dialect; a partial stamp, one contradicting the registered policy
112
+ or the execution snapshot's backend, or an absent stamp with an unregistered
113
+ execution backend, refuses with the new
114
+ `terrine.metadata.invalid_engine_stamp` (`reason`, present `fields`) instead
115
+ of acquiring verified status.
116
+ - Centralize verified engine admission, capability records and expression
117
+ selection without changing engine execution behavior, supported builds,
118
+ session lifetimes or connection ownership.
119
+ Unsupported-operation refusal routes now name registered verified engines
120
+ implementing the operation, correcting stale DuckDB/PostgreSQL-only advice;
121
+ exact-ratio routes name PostgreSQL and BigQuery. Refusal codes and non-route
122
+ context are unchanged.
123
+ Admission gates and static inspection both honor the selected provider's
124
+ effective capability evidence; unavailable dialect admission still refuses
125
+ before materialization.
126
+ - Encapsulate native numeric types, exact arithmetic and float conversion in
127
+ each engine's expression policy. Shared normalization retains the existing
128
+ coefficient bounds, NULL/nonfinite flags, overflow behavior and rounding
129
+ tolerances.
130
+ - Encapsulate timestamp precision, UTC conversion, infinity ordering and exact
131
+ output narrowing in each engine's expression policy. Explicit timestamp
132
+ selection also governs nested operations without changing the enclosing
133
+ expression scope; temporal windows and cohort bucket remainders are unchanged.
134
+ - Centralize as-of joins, NULL-ignoring extrema, integer sequences and feature
135
+ admission in the registered engine policies. Strict/inclusive cutoffs, duplicate
136
+ requests, aliases and existing feature/build boundaries are unchanged.
137
+
138
+ ### Fixed
139
+
140
+ - The registered DuckDB, PostgreSQL and Snowflake UTC conversions treated a
141
+ naive timestamp as a value to attach UTC to, so the wall clock they returned
142
+ was zone-aware and any date read from it followed the session time zone: the
143
+ start of the day containing naive `2025-01-01 00:00:00` came out as
144
+ `2024-12-31` in an `America/New_York` session. A naive value is its own UTC
145
+ wall clock and is now returned unchanged, and a UTC-aware value is its own
146
+ instant. Dataset features were not affected: anchors are UTC-aware and naive
147
+ source timestamps enter through the instant conversion; the wrong answer was
148
+ reachable only by applying the wall-clock conversion directly to a naive
149
+ value, as endpoint alignment on naive values now does.
150
+ - Source distributions no longer include internal planning documents or local
151
+ build and test output.
152
+
153
+ ### Tests
154
+
155
+ - Centralize known-answer engine conformance cases for whole-day schedules,
156
+ NULL-ignoring extrema, cancellation-safe float and exact decimal windows,
157
+ UTC timestamp conversion, tied event ordering, censor times and strict as-of
158
+ cutoffs. Run the same cases on DuckDB and live PostgreSQL. Add the day
159
+ start of a naive timestamp in a non-UTC session, with a day-aligned feature
160
+ over naive source events.
161
+ - Compile the same known answers with the portable base expression policy on
162
+ each real connection under `--generic-dialect` (DuckDB by default, PostgreSQL
163
+ with `-m postgres`), about twenty seconds per backend inside the existing
164
+ integration and `make test-postgres` checks, and check the policy's
165
+ numerical and relational contracts against independent oracles on both, plus
166
+ real unregistered-backend reads and refusals on the Ibis SQLite backend.
167
+
168
+ ### Documentation
169
+
170
+ - Describe the verified, opted-in unverified and refused backend tiers, the
171
+ portable numerical contract and its admission witnesses, and the engine
172
+ stamp in the compatibility, capabilities and public API guides. Add
173
+ `examples/unverified_backend.py`, which opts in to the Ibis SQLite backend,
174
+ shows the default refusal, a coded operation refusal, a real read and its
175
+ stamp.
176
+ - Rewrite the README around a customer spend forecasting table built from
177
+ pandas DataFrames, with the shapes Terrine builds and how to read its output.
178
+ The first-dataset guide follows the same example.
179
+
180
+ ### Public API
181
+
182
+ - `EventAnchor(..., offset="1d")` shifts analysis time zero by a non-negative
183
+ elapsed duration for landmark datasets, including survival after a baseline
184
+ feature period. First/nth selection and `anchor_range` retain original-event
185
+ semantics. Nonzero offsets participate in anchor identity, provenance and
186
+ Design hashes; zero-offset serialization and hashes remain unchanged.
187
+ Negative offsets refuse with `terrine.design.invalid`; persisted-layer
188
+ substitution for shifted anchors refuses before source access with
189
+ `terrine.compile.runtime_shape_unsupported`. Recipes are unchanged.
190
+
191
+ ### Breaking changes
192
+
193
+ - Constructing `Provenance` requires `engine_name`, `dialect` and
194
+ `engine_status`; saved payloads without them still decode as their recorded
195
+ verified execution dialect.
196
+
197
+ ## 0.1.0.dev0
4
198
 
5
199
  ### Documentation
6
200
 
@@ -27,9 +27,11 @@ test-scale:
27
27
  # Live PostgreSQL probes; requires TERRINE_TEST_POSTGRES_* settings and fails
28
28
  # without them. `uv run python -m tests.integration.postgres_scratch start` starts
29
29
  # a throwaway local server and prints the settings. The extras provide the driver
30
- # and the DuckDB reference backend.
30
+ # and the DuckDB reference backend. The second run compiles the same conformance
31
+ # known answers through the portable base expression policy on the server.
31
32
  test-postgres:
32
33
  uv run --extra duckdb --extra postgres pytest integration -m postgres
34
+ uv run --extra duckdb --extra postgres pytest integration/conformance --generic-dialect -m postgres
33
35
 
34
36
  # Live BigQuery probes; requires TERRINE_TEST_BIGQUERY_PROJECT/_DATASET and
35
37
  # credentials (GOOGLE_APPLICATION_CREDENTIALS, application-default, or
@@ -0,0 +1,201 @@
1
+ Metadata-Version: 2.5
2
+ Name: terrine
3
+ Version: 0.1.0.dev1
4
+ Summary: Typed dataset construction and analytical workflows
5
+ Project-URL: Homepage, https://github.com/kylejcaron/terrine
6
+ Project-URL: Documentation, https://github.com/kylejcaron/terrine/tree/main/docs/guides
7
+ Project-URL: Repository, https://github.com/kylejcaron/terrine
8
+ Project-URL: Changelog, https://github.com/kylejcaron/terrine/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/kylejcaron/terrine/issues
10
+ License-Expression: Apache-2.0
11
+ License-File: LICENSE
12
+ License-File: NOTICE
13
+ License-File: THIRD_PARTY_NOTICES.txt
14
+ Requires-Python: <3.15,>=3.13
15
+ Requires-Dist: ibis-framework<13,>=11
16
+ Requires-Dist: numpy<3,>=2.1
17
+ Requires-Dist: pyarrow<24,>=18
18
+ Requires-Dist: pydantic<3,>=2.8.1
19
+ Requires-Dist: pyyaml<7,>=6.0.2
20
+ Requires-Dist: sqlglot<30,>=29
21
+ Provides-Extra: airflow
22
+ Requires-Dist: apache-airflow-providers-standard==1.20.1; extra == 'airflow'
23
+ Requires-Dist: apache-airflow==3.3.2; extra == 'airflow'
24
+ Provides-Extra: bigquery
25
+ Requires-Dist: ibis-framework[bigquery]<13,>=12; extra == 'bigquery'
26
+ Provides-Extra: dagster
27
+ Requires-Dist: dagster==1.13.26; extra == 'dagster'
28
+ Provides-Extra: dbt
29
+ Requires-Dist: dbt-core==1.10.13; extra == 'dbt'
30
+ Requires-Dist: dbt-duckdb==1.9.6; extra == 'dbt'
31
+ Provides-Extra: diff-diff
32
+ Requires-Dist: diff-diff==3.13.0; extra == 'diff-diff'
33
+ Provides-Extra: doubleml
34
+ Requires-Dist: doubleml==0.11.4; extra == 'doubleml'
35
+ Provides-Extra: dowhy
36
+ Requires-Dist: dowhy==0.14; (python_version < '3.14') and extra == 'dowhy'
37
+ Provides-Extra: duckdb
38
+ Requires-Dist: duckdb==1.5.6; extra == 'duckdb'
39
+ Requires-Dist: ibis-framework[duckdb]<13,>=11; extra == 'duckdb'
40
+ Provides-Extra: duckreg
41
+ Requires-Dist: duckreg==0.4.1; extra == 'duckreg'
42
+ Provides-Extra: increment
43
+ Requires-Dist: increment==0.1.0a2; extra == 'increment'
44
+ Provides-Extra: lifelines
45
+ Requires-Dist: lifelines==0.30.3; extra == 'lifelines'
46
+ Provides-Extra: pandas
47
+ Requires-Dist: pandas<4,>=2.2; extra == 'pandas'
48
+ Provides-Extra: polars
49
+ Requires-Dist: polars<2,>=1.21; extra == 'polars'
50
+ Provides-Extra: postgres
51
+ Requires-Dist: ibis-framework[postgres]<13,>=12; extra == 'postgres'
52
+ Provides-Extra: sketches
53
+ Requires-Dist: datasketches<6,>=5.2; extra == 'sketches'
54
+ Provides-Extra: snowflake
55
+ Requires-Dist: ibis-framework[snowflake]<13,>=12; extra == 'snowflake'
56
+ Description-Content-Type: text/markdown
57
+
58
+ # terrine
59
+
60
+ **Build datasets directly from event logs.**
61
+
62
+ You have event tables: orders, sessions, payments. You want a modeling table, for example one row per customer per snapshot date, with features computed from the past and labels from the future. Terrine builds that table in your warehouse (DuckDB, PostgreSQL, BigQuery, Snowflake, and more) from a few lines of Python.
63
+
64
+ ## Why Terrine?
65
+
66
+ Most data science datasets come in a few shapes:
67
+
68
+ - snapshots with features and labels;
69
+ - cohort curves;
70
+ - time-to-event tables;
71
+ - before-and-after windows around an event.
72
+
73
+ Each time, the SQL is the same pile of date joins and window boundaries, rewritten by hand. Terrine takes the shape as a starting point. You name the rows you want and the windows to aggregate over; Terrine writes and runs the queries.
74
+
75
+ Because Terrine knows each row's snapshot time, features can only look backwards and targets only forwards. Each value also says whether its window was fully covered by your data, so you decide how to treat gaps for your problem.
76
+
77
+ ## Example: a customer spend forecasting table
78
+
79
+ Requires Python 3.13 or 3.14.
80
+
81
+ ```sh
82
+ pip install --pre "terrine[duckdb,pandas]"
83
+ ```
84
+
85
+ <!-- --8<-- [start:first-dataset] -->
86
+ ```python
87
+ import ibis
88
+ import pandas as pd
89
+ import terrine as tr
90
+
91
+ customers = pd.DataFrame({"customer_id": ["alice", "bob", "carol"]})
92
+ orders = pd.DataFrame({
93
+ "customer_id": ["alice", "alice", "alice", "alice", "bob"],
94
+ "ordered_at": pd.to_datetime(
95
+ ["2025-01-03", "2025-01-20", "2025-02-10", "2025-03-05", "2025-01-05"]
96
+ ),
97
+ "amount": [30.0, 45.0, 25.0, 60.0, 80.0],
98
+ })
99
+
100
+ con = ibis.duckdb.connect() # or your warehouse: ibis.bigquery.connect(...), ...
101
+ con.create_table("customers", customers)
102
+ con.create_table("orders", orders)
103
+
104
+ # Tell Terrine who the rows are about and which dates the orders table covers.
105
+ model = tr.Model.from_tables(
106
+ con,
107
+ units={"customer": tr.UnitSource("customers", "customer_id")},
108
+ events={"orders": tr.Events(
109
+ "orders", "ordered_at", {"customer": "customer_id"},
110
+ values=("amount",), complete=("2025-01-01", "2025-03-20"),
111
+ )},
112
+ )
113
+
114
+ # One row per customer every 28 days: the last 30 days as features,
115
+ # spend in the next 30 days as the target.
116
+ design = tr.recipes.time_series(
117
+ "customer", every="28d", start="2025-02-01", end="2025-03-02",
118
+ features={
119
+ "orders_30d": tr.agg("orders_count", window=("-30d", "0s")),
120
+ "spend_30d": tr.agg("orders_amount_sum", window=("-30d", "0s")),
121
+ },
122
+ outcomes={
123
+ "spend_next_30d": tr.agg("orders_amount_sum", window=("0s", "30d")),
124
+ },
125
+ )
126
+
127
+ data = design.build(model).to_pandas()
128
+ columns = ["unit", "t0", "orders_30d", "spend_30d", "spend_next_30d", "spend_next_30d__reason"]
129
+ print(data[columns].assign(t0=data["t0"].dt.date).sort_values(["t0", "unit"]).to_string(index=False))
130
+ ```
131
+ <!-- --8<-- [end:first-dataset] -->
132
+
133
+ ```text
134
+ unit t0 orders_30d spend_30d spend_next_30d spend_next_30d__reason
135
+ alice 2025-02-01 2 75.0 25.0 <NA>
136
+ bob 2025-02-01 1 80.0 0.0 <NA>
137
+ carol 2025-02-01 0 0.0 0.0 <NA>
138
+ alice 2025-03-01 1 25.0 <NA> incomplete_coverage
139
+ bob 2025-03-01 0 0.0 <NA> incomplete_coverage
140
+ carol 2025-03-01 0 0.0 <NA> incomplete_coverage
141
+ ```
142
+
143
+ (Only some columns are printed; `t0` is shown as a date.)
144
+
145
+ Reading the output:
146
+
147
+ - **Carol has no orders.** She still has a row at each snapshot, with zero counts.
148
+ - **Alice's February 10 order** is in her February 1 target and her March 1 features. The same order is the future for one row and the past for the next.
149
+ - **The March targets are unknown.** Their 30-day window runs past March 20, where the orders data ends. Drop them, keep them for scoring, or handle them however suits your model.
150
+
151
+ Terrine names metrics after your table and columns: `orders_count`, `orders_amount_sum` and `orders_amount_mean`. Every value has a `<name>__complete` flag and a `<name>__reason` column, so you can filter or impute deliberately.
152
+
153
+ ## The four ideas
154
+
155
+ | Idea | In the example |
156
+ | --- | --- |
157
+ | **Unit:** who each row is about, read from a table so nobody silently drops out | `customers.customer_id` |
158
+ | **t0:** the snapshot time of each row | every 28 days from 2025-02-01 |
159
+ | **Windows:** relative to t0, start included, end excluded. Features must end at or before t0; outcomes must start at or after t0 | `("-30d", "0s")` is the 30 days before t0; `("0s", "30d")` is the 30 days after |
160
+ | **Completeness:** the calendar days, inclusive, that you declare fully loaded for each event table (not just the first and last event you happen to have). Windows outside them are unknown, not zero | `complete=("2025-01-01", "2025-03-20")` |
161
+
162
+ ## Other datasets
163
+
164
+ `tr.recipes` covers the usual shapes. Each one returns a normal `Design` you can edit.
165
+
166
+ | You want | Recipe | Guide |
167
+ | --- | --- | --- |
168
+ | Forecasting, propensity and other features-plus-target tables | `time_series` | [Scheduled time series](https://github.com/kylejcaron/terrine/blob/main/docs/guides/temporal-engine.md#scheduled-time-series) |
169
+ | Customer lifetime value curves by cohort | `ltv` | [Cumulative LTV](https://github.com/kylejcaron/terrine/blob/main/docs/guides/trajectories-survival.md#cumulative-ltv) |
170
+ | Time-to-event / survival analysis | `survival`, `life_table` | [Survival records](https://github.com/kylejcaron/terrine/blob/main/docs/guides/trajectories-survival.md#survival-records) |
171
+ | Behavior before and after an event (event study) | `event_panel` | [Event panels](https://github.com/kylejcaron/terrine/blob/main/docs/guides/sequential-panels.md#event-panels) |
172
+ | Repeated eligibility for observational "trials" | `trial_panel` | [Trial panels](https://github.com/kylejcaron/terrine/blob/main/docs/guides/sequential-panels.md#scheduled-trial-panels) |
173
+ | State changes such as plan upgrades and downgrades | `state_panel` | [State panels](https://github.com/kylejcaron/terrine/blob/main/docs/guides/state-panels.md) |
174
+ | Ordered events in a window after an event, up to a set count | `event_sequence` | [Event sequences](https://github.com/kylejcaron/terrine/blob/main/docs/guides/temporal-engine.md#bounded-event-sequences-and-event-time-origins) |
175
+
176
+ More feature recipes are in [common feature patterns](https://github.com/kylejcaron/terrine/blob/main/docs/guides/common-feature-patterns.md): recency, "active in the last N days" filters, attributes as of t0, conversions after a purchase, and true/false labels.
177
+
178
+ ## Where it runs
179
+
180
+ Queries run where your data lives; nothing is uploaded. Results come back as pandas, Polars or Arrow.
181
+
182
+ | Backend | Status |
183
+ | --- | --- |
184
+ | DuckDB | Reference backend; can also save versioned datasets |
185
+ | PostgreSQL 16, BigQuery, Snowflake | Verified against DuckDB; floating-point results match within documented tolerances |
186
+ | Other Ibis backends | Read-only, unverified; opt in with `allow_unverified=True` |
187
+
188
+ See [backend compatibility](https://github.com/kylejcaron/terrine/blob/main/docs/guides/backend-compatibility.md) for extras and details.
189
+
190
+ ## Learn more
191
+
192
+ - [Your first dataset](https://github.com/kylejcaron/terrine/blob/main/docs/guides/first-dataset.md): this example step by step, with output limits and saved datasets.
193
+ - [Training and scoring sets](https://github.com/kylejcaron/terrine/blob/main/docs/guides/ml-staging.md): reproducible train/score tables and saved definitions.
194
+ - [Library hand-offs](https://github.com/kylejcaron/terrine/blob/main/docs/guides/library-handoffs.md): pass datasets to modeling libraries. Terrine builds data and makes no causal claims.
195
+ - [All guides](https://github.com/kylejcaron/terrine/blob/main/docs/guides/index.md), [limitations](https://github.com/kylejcaron/terrine/blob/main/docs/guides/limitations.md) and [error codes](https://github.com/kylejcaron/terrine/blob/main/docs/reference/refusals.md).
196
+
197
+ Contributing? See [CONTRIBUTING.md](https://github.com/kylejcaron/terrine/blob/main/CONTRIBUTING.md).
198
+
199
+ ## License
200
+
201
+ [Apache License 2.0](https://github.com/kylejcaron/terrine/blob/main/LICENSE); see [NOTICE](https://github.com/kylejcaron/terrine/blob/main/NOTICE).