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.
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/ci.yml +4 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.gitignore +1 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/AGENTS.md +58 -9
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/CHANGELOG.md +195 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/Makefile +3 -1
- terrine-0.1.0.dev1/PKG-INFO +201 -0
- terrine-0.1.0.dev1/README.md +144 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/backend-compatibility.md +264 -18
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/candidate-anchors.md +37 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/capabilities.md +21 -15
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/common-stack-interop.md +20 -6
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/design-specs.md +9 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/first-dataset.md +37 -20
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/public-api.md +33 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/trajectories-survival.md +34 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/reference/refusals.md +5 -3
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/first_dataset.py +1 -1
- terrine-0.1.0.dev1/examples/unverified_backend.py +150 -0
- terrine-0.1.0.dev1/integration/backends/test_engine_registry.py +25 -0
- terrine-0.1.0.dev1/integration/backends/test_generic.py +805 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_time_to_event.py +40 -0
- terrine-0.1.0.dev1/integration/conformance/test_engines.py +341 -0
- terrine-0.1.0.dev1/integration/conftest.py +24 -0
- terrine-0.1.0.dev1/integration/execution/test_generic_provider.py +439 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/conftest.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_backend_capabilities.py +6 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_ml_staging_examples.py +15 -17
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_admission.py +3 -2
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_execution.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_shapes.py +19 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_windows.py +0 -17
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/pyproject.toml +4 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tach.toml +5 -5
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/__init__.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/adapters/increment.py +10 -5
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/design_schema.py +2 -1
- terrine-0.1.0.dev1/terrine/backends/capabilities.py +64 -0
- terrine-0.1.0.dev1/terrine/backends/dialect.py +716 -0
- terrine-0.1.0.dev1/terrine/backends/engines/bigquery.py +192 -0
- terrine-0.1.0.dev1/terrine/backends/engines/duckdb.py +301 -0
- terrine-0.1.0.dev1/terrine/backends/engines/postgres.py +355 -0
- terrine-0.1.0.dev1/terrine/backends/engines/snowflake.py +277 -0
- terrine-0.1.0.dev1/terrine/backends/extrema.py +25 -0
- terrine-0.1.0.dev1/terrine/backends/joins.py +41 -0
- terrine-0.1.0.dev1/terrine/backends/numeric.py +476 -0
- terrine-0.1.0.dev1/terrine/backends/registry.py +284 -0
- terrine-0.1.0.dev1/terrine/backends/sequences.py +14 -0
- terrine-0.1.0.dev1/terrine/backends/timestamps.py +234 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/_asof.py +5 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/anchors.py +18 -9
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/columns.py +29 -5
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/cost.py +8 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/dispatch.py +10 -4
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/layers.py +1 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/materialize.py +1 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/_observations.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/cohorts.py +30 -12
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/event_sequence.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/ltv.py +34 -8
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/matched_t0.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/time_to_event.py +4 -6
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/data.py +80 -2
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/reference.py +4 -5
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/errors.py +5 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_bigquery.py +81 -14
- terrine-0.1.0.dev1/terrine/execution/_generic.py +385 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_native.py +57 -8
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_postgres.py +90 -82
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_publish.py +1 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_snowflake.py +93 -23
- terrine-0.1.0.dev1/terrine/execution/backend.py +100 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/inputs.py +62 -19
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/lifelines.py +1 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/preflight.py +2 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/frame.py +19 -4
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/model.py +82 -11
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/api.py +4 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/backends/duckdb.py +2 -2
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/planning.py +1 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/snapshot.py +1 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/bind.py +26 -12
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/io.py +1 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/rules.py +2 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/sql.py +90 -26
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/anchors.py +24 -9
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/io.py +4 -2
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/validate.py +9 -4
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/aggregate.py +43 -46
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/attributes.py +65 -45
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/daily.py +23 -34
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/first.py +6 -28
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/prefix.py +42 -8
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/.test_durations +0 -3
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_schemas.py +1 -0
- terrine-0.1.0.dev1/tests/backends/test_engine_registry.py +225 -0
- terrine-0.1.0.dev1/tests/backends/test_generic.py +457 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/backends/test_numeric.py +5 -5
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/backends/test_sequences.py +1 -1
- terrine-0.1.0.dev1/tests/backends/test_timestamps.py +120 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_anchors.py +34 -2
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_cohorts.py +17 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_columns.py +58 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_event_sequence.py +0 -16
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_ltv.py +63 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_persisted_layers.py +25 -10
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_training_view.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_winsorization_definition.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/documentation.py +5 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/execution/test_backend_provider.py +129 -27
- terrine-0.1.0.dev1/tests/execution/test_generic.py +311 -0
- terrine-0.1.0.dev1/tests/execution/test_sql_verification.py +106 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/conftest.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_warehouse_preflight.py +21 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/baseline.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/test_baseline.py +3 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_backend_capabilities.py +2 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_layers.py +5 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_planning.py +6 -3
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/semantic/test_metric_rules.py +17 -1
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/semantic/test_rules_import.py +5 -1
- terrine-0.1.0.dev1/tests/semantic/test_sql.py +94 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_design_io.py +41 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_nonadditive_spec.py +31 -1
- terrine-0.1.0.dev1/tests/temporal/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_first_occurrence.py +21 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_scd2_attributes.py +19 -22
- terrine-0.1.0.dev1/tests/test_engine_dispatch.py +401 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_frame_runtime.py +10 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_metadata.py +203 -32
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_optional_dependencies.py +1 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/uv.lock +1 -1
- terrine-0.1.0.dev0/PKG-INFO +0 -164
- terrine-0.1.0.dev0/README.md +0 -107
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-build.md +0 -416
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-dataset-workflows.md +0 -164
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-study-api.md +0 -312
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-16-terrine-temporal.md +0 -345
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-17-terrine-frame.md +0 -182
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-event-sequences.md +0 -200
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-materialization-runtime.md +0 -267
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-ml-staging.md +0 -201
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-orchestrators.md +0 -131
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-sequential-panels.md +0 -183
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-trajectories-survival.md +0 -269
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-18-terrine-warehouse-scale.md +0 -163
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-agent-tools.md +0 -181
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-fast-tests-inventory.md +0 -478
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-fast-tests-ledger.md +0 -561
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-fast-tests.md +0 -145
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-interop.md +0 -202
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-library-handoffs.md +0 -81
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-19-terrine-release-readiness.md +0 -100
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-20-terrine-test-tiers.md +0 -113
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-21-terrine-postgres-execution.md +0 -170
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-22-bigquery-execution.md +0 -66
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-22-ibis-study-queries.md +0 -73
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-22-postgres-float-windows.md +0 -56
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-23-exact-float-windows.md +0 -88
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-23-snowflake-execution.md +0 -323
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-24-mutation-audit.md +0 -80
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-24-test-suite-speed.md +0 -68
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-26-public-readiness.md +0 -255
- terrine-0.1.0.dev0/docs/superpowers/plans/2026-10-26-table-first-binding-and-recipes.md +0 -184
- terrine-0.1.0.dev0/docs/superpowers/plans/ledger-compile.md +0 -345
- terrine-0.1.0.dev0/docs/superpowers/plans/ledger-dataset.md +0 -136
- terrine-0.1.0.dev0/docs/superpowers/plans/ledger-runtime.md +0 -180
- terrine-0.1.0.dev0/docs/superpowers/plans/ledger-test-tiers-misc.md +0 -14
- terrine-0.1.0.dev0/docs/superpowers/plans/ledger-test-tiers-state-panels.md +0 -37
- terrine-0.1.0.dev0/docs/superpowers/plans/ledger-test-tiers-temporal.md +0 -29
- terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-16-terrine-handoff.md +0 -635
- terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-17-terrine-frame-design.md +0 -135
- terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-18-terrine-scope-datasets-only.md +0 -76
- terrine-0.1.0.dev0/docs/superpowers/specs/2026-10-26-table-first-binding-and-recipes-design.md +0 -146
- terrine-0.1.0.dev0/integration/conftest.py +0 -1
- terrine-0.1.0.dev0/terrine/backends/capabilities.py +0 -117
- terrine-0.1.0.dev0/terrine/backends/dialect.py +0 -55
- terrine-0.1.0.dev0/terrine/backends/extrema.py +0 -40
- terrine-0.1.0.dev0/terrine/backends/joins.py +0 -75
- terrine-0.1.0.dev0/terrine/backends/numeric.py +0 -657
- terrine-0.1.0.dev0/terrine/backends/sequences.py +0 -40
- terrine-0.1.0.dev0/terrine/backends/timestamps.py +0 -421
- terrine-0.1.0.dev0/terrine/execution/backend.py +0 -349
- terrine-0.1.0.dev0/tests/backends/test_timestamps.py +0 -62
- terrine-0.1.0.dev0/tests/semantic/test_sql.py +0 -32
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/CLA.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/cla.yml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/release.yml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/scale.yml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.github/workflows/warehouse-backends.yml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.kata.toml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.pre-commit-config.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.python-version +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/.roborev.toml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/CLAUDE.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/CONTRIBUTING.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/LICENSE +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/NOTICE +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/THIRD_PARTY_NOTICES.txt +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/README.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/report.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/results/duckdb-scale-v2-small.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/run_duckdb.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/run_live.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/run_overhead.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/workloads/small.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/benchmarks/workloads.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/common-feature-patterns.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/cost-and-residency.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/dataset-agent-tools.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/design-validation.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/increment-compatibility.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/index.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/library-handoffs.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/limitations.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/materialization-runtime.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/ml-staging.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/orchestrators.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/semantic-layer-interop.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/sequential-panels.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/shared-dataset-contract.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/state-panels.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/synthetic-worlds.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/temporal-attributes.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/guides/temporal-engine.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/index.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/edge-case-designs.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/landscape.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/ohdsi-study-design.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/docs/research/point-in-time-feature-apis.md +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/evals/agent_dataset_tools.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/batch_scoring.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/bigquery/dataset.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/common_stack_interop.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/dataset_agent_tools.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/event_sequences.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/increment_parity.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/materialization_runtime.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/ml_staging.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/nonadditive_metrics.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/orchestrators/airflow_materialization.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/orchestrators/dagster_materialization.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/orchestrators/dbt_core_after_build.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/postgres/dataset.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/retention_and_winsorization.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/retention_metrics.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/sequential_trials.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/snowflake/dataset.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/state_panel.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/table_first_dataset.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/examples/trajectories_survival.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/.test_durations +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/agent/test_dataset_tool_example.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/agent/test_dataset_tool_flow.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/agent/test_dataset_tool_runtime.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/backends/test_numeric.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compat/test_native_catalog.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_boolean_outputs.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_column_runtime_contracts.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_frame_runtime_window_export.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_ltv.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/compile/test_retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/test_diff_diff.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/test_library_handoffs.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/handoffs/test_lifelines.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/parity/test_increment_panel_parity.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/state_panels/test_state_panel_workflows.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/temporal/test_retention_bands.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/temporal/test_window_quotient_composition.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/test_independence.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/test_model_from_tables.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/bigquery_fixtures.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/postgres_fixtures.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/snowflake_fixtures.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_bigquery_admission.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_bigquery_execution.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_bigquery_parity.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_duckdb_compiler_scale.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_duckdb_runtime_recovery.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_duckdb_scale.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_incremental_equivalence.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_live_warehouse_scale.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_materialization_example.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_ml_staging_duckdb.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_orchestrator_adapters.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_planning.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_postgres_timestamps.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_request_replay_process.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_api.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_cli.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_control.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_runtime_cost_budget.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_saved_plan_restart.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snapshot_capture_bounds.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snapshot_partition_consistency.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snowflake_admission.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snowflake_execution.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_snowflake_parity.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/integration/warehouse_execution/test_winsorization_definition.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/mkdocs.yml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/scripts/mutate.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/_time.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/adapters/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/adapters/opentelemetry.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/definitions.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/policy.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/schemas.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/agent/tools.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/backends/__init__.py +0 -0
- {terrine-0.1.0.dev0/tests → terrine-0.1.0.dev1/terrine/backends/engines}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/_predicates.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/provenance.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/ranges.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/calendar_comparison.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/event_panel.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/life_table.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/sequential.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/compile/shapes/state_panel.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/export.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/manifest.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/splits.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/training.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/dataset/transforms.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/_metadata.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/execution/gates.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/diff_diff.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/export/surrogate.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/orchestration/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/orchestration/airflow.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/orchestration/dagster.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/py.typed +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/recipes.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/arrow.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/backends/base.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/changes.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/cli.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/control.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/cost.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/events.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/execute.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/gc.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/graph.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/layers.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/leases.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/materialize.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/owner.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/planner.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/saved_plan.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/state.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/status.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/runtime/synchronization.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/models.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/semantic/time.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/baseline.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/generate.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/scale.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/simulate/world.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/dataset.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/design.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/expressions.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/panels.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/sequence.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/state_panel.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/trajectory.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/spec/transforms.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/lookup.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/nonadditive.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/ranges.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/readiness.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/sequence.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/sessionize.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/temporal/sketches.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/terrine/windows.py +0 -0
- {terrine-0.1.0.dev0/tests/backends → terrine-0.1.0.dev1/tests}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/_timing.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/_worlds.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/adapters/test_airflow.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/adapters/test_dagster.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/adapters/test_dbt_core_after_build.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/conftest.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/fixtures/catalog.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_inspect_design.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_materialize.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_plan_authorization.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_policy.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/agent/test_requests.py +0 -0
- {terrine-0.1.0.dev0/tests/compat → terrine-0.1.0.dev1/tests/backends}/__init__.py +0 -0
- {terrine-0.1.0.dev0/tests/compile → terrine-0.1.0.dev1/tests/compat}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/catalog.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/increment.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/same-name-collision.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/fixtures/unsupported-metric.yaml +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/test_increment_semantics.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compat/test_semantic_interop.py +0 -0
- {terrine-0.1.0.dev0/tests/dataset → terrine-0.1.0.dev1/tests/compile}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_completeness_columns.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_cost_estimates.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_matched_t0.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_nonadditive.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_panels.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_range_scaling.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_state_panel.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_survival_bounds.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_time_to_event.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/compile/test_winsorization.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/conftest.py +0 -0
- {terrine-0.1.0.dev0/tests/execution → terrine-0.1.0.dev1/tests/dataset}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_definition.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_label_support.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_manifest.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_runtime_staging.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_splits.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/dataset/test_staging.py +0 -0
- {terrine-0.1.0.dev0/tests/fixtures → terrine-0.1.0.dev1/tests/execution}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/execution/test_inputs.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/execution/test_snowflake_session.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_diff_diff.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_lifelines.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_preflight.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/export/test_surrogate.py +0 -0
- {terrine-0.1.0.dev0/tests/integration → terrine-0.1.0.dev1/tests/fixtures}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/interop.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/materialized_frame_v1.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_definition.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_fitted_definition.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_fitted_transform.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_invocation.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_manifest.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_plan_request.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/pre_rename_saved_plan.json +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/fixtures/winsorization.py +0 -0
- {terrine-0.1.0.dev0/tests/parity → terrine-0.1.0.dev1/tests/integration}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/distribution_smoke.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/postgres_scratch.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/study_api_smoke.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/test_benchmark_failure_reporting.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/integration/test_live_scale_gate.py +0 -0
- {terrine-0.1.0.dev0/tests/reference → terrine-0.1.0.dev1/tests/parity}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/parity/test_increment_frames.py +0 -0
- {terrine-0.1.0.dev0/tests/runtime → terrine-0.1.0.dev1/tests/reference}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/baseline.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/nonadditive.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/panels.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/ranges.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/scale_metrics.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/test_baseline.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/test_event_sequences.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/reference/test_panels.py +0 -0
- {terrine-0.1.0.dev0/tests/semantic → terrine-0.1.0.dev1/tests/runtime}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_api.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_arrow_output.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_batch_cleanup.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_cancel_publication.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_cli.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_control_ipc.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_control_recovery.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_cost_estimates.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_duckdb_publication.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_execute.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_gc.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_graph.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_imports.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_invalidation.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_leases.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_opentelemetry.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_planner.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_recovery_lease.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_request_replay.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_run_events.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_run_status.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_saved_plan.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_snapshot.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_snapshot_lifecycle.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/runtime/test_state.py +0 -0
- {terrine-0.1.0.dev0/tests/simulate → terrine-0.1.0.dev1/tests/semantic}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/semantic/test_catalog.py +0 -0
- {terrine-0.1.0.dev0/tests/spec → terrine-0.1.0.dev1/tests/simulate}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/simulate/test_scale_generator.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/simulate/test_world.py +0 -0
- {terrine-0.1.0.dev0/tests/temporal → terrine-0.1.0.dev1/tests/spec}/__init__.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_calendar_comparison.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_expressions.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_panels.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_recipes.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_sequence.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_sketch_refusal.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_state_panel.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/spec/test_validate.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_nonadditive.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_prefix_lookup.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_range_scaling.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_readiness.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_retention.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_semantic_binding.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sequence.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sessionize.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sketches.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_sparse_state.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/temporal/test_window_aggregates.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_common_stack_interop.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_distribution.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_documentation.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_errors.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_frame.py +0 -0
- {terrine-0.1.0.dev0 → terrine-0.1.0.dev1}/tests/test_mutation_runner.py +0 -0
- {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
|
|
@@ -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)
|
|
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,
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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/`),
|
|
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
|
-
##
|
|
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).
|