dblect 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dblect-0.1.0/.github/workflows/ci.yml +65 -0
- dblect-0.1.0/.github/workflows/release.yml +47 -0
- dblect-0.1.0/.gitignore +225 -0
- dblect-0.1.0/CHANGELOG.md +134 -0
- dblect-0.1.0/CLAUDE.md +46 -0
- dblect-0.1.0/LICENSE +201 -0
- dblect-0.1.0/PKG-INFO +443 -0
- dblect-0.1.0/README.md +211 -0
- dblect-0.1.0/docs/README.md +31 -0
- dblect-0.1.0/docs/current_state/architecture.md +429 -0
- dblect-0.1.0/docs/current_state/capabilities.md +77 -0
- dblect-0.1.0/docs/current_state/incremental-worlds.md +53 -0
- dblect-0.1.0/docs/dblect-overview.md +29 -0
- dblect-0.1.0/docs/design/column-level-lineage.md +184 -0
- dblect-0.1.0/docs/design/config-and-flag-worlds.md +319 -0
- dblect-0.1.0/docs/design/contract-directed-generation.md +149 -0
- dblect-0.1.0/docs/design/dblect_technical_intro.md +651 -0
- dblect-0.1.0/docs/design/declaration-dsl.md +600 -0
- dblect-0.1.0/docs/design/demo_walkthrough.md +487 -0
- dblect-0.1.0/docs/design/design-concepts-digest.md +190 -0
- dblect-0.1.0/docs/design/domain-type-algebra.md +274 -0
- dblect-0.1.0/docs/design/domain-type-functions.md +90 -0
- dblect-0.1.0/docs/design/domain-type-maybe.md +107 -0
- dblect-0.1.0/docs/design/domain-type-substrate-readiness.md +45 -0
- dblect-0.1.0/docs/design/dsl-reference.md +251 -0
- dblect-0.1.0/docs/design/flags_and_configs_as_types.md +312 -0
- dblect-0.1.0/docs/design/hazard-algebra.md +187 -0
- dblect-0.1.0/docs/design/lineage-facts-types.md +361 -0
- dblect-0.1.0/docs/design/lineage-facts.md +284 -0
- dblect-0.1.0/docs/design/nullability-hazards.md +109 -0
- dblect-0.1.0/docs/design/propagation-soundness.md +109 -0
- dblect-0.1.0/docs/design/research/dbt-config-patterns.md +257 -0
- dblect-0.1.0/docs/design/research/variational-analysis-literature.md +72 -0
- dblect-0.1.0/docs/design/tiers_and_rough_implementation_order.md +329 -0
- dblect-0.1.0/docs/design/var-inference-spec.md +463 -0
- dblect-0.1.0/pyproject.toml +157 -0
- dblect-0.1.0/questions_and_decisions.md +449 -0
- dblect-0.1.0/scripts/refresh_bigquery_fixtures.sh +58 -0
- dblect-0.1.0/scripts/refresh_jaffle_fixtures.sh +58 -0
- dblect-0.1.0/scripts/refresh_scenarios.sh +84 -0
- dblect-0.1.0/scripts/refresh_snapshot_audit.sh +67 -0
- dblect-0.1.0/src/dblect/__init__.py +28 -0
- dblect-0.1.0/src/dblect/_version.py +1 -0
- dblect-0.1.0/src/dblect/adapters/__init__.py +40 -0
- dblect-0.1.0/src/dblect/adapters/builtin/__init__.py +7 -0
- dblect-0.1.0/src/dblect/adapters/builtin/bigquery.py +18 -0
- dblect-0.1.0/src/dblect/adapters/builtin/duckdb.py +24 -0
- dblect-0.1.0/src/dblect/adapters/builtin/postgres.py +17 -0
- dblect-0.1.0/src/dblect/adapters/builtin/redshift.py +17 -0
- dblect-0.1.0/src/dblect/adapters/builtin/snowflake.py +16 -0
- dblect-0.1.0/src/dblect/adapters/model.py +87 -0
- dblect-0.1.0/src/dblect/adapters/registry.py +108 -0
- dblect-0.1.0/src/dblect/analysis.py +125 -0
- dblect-0.1.0/src/dblect/audit/__init__.py +26 -0
- dblect-0.1.0/src/dblect/audit/sourcemap.py +177 -0
- dblect-0.1.0/src/dblect/audit/suppress.py +273 -0
- dblect-0.1.0/src/dblect/audit/walker.py +307 -0
- dblect-0.1.0/src/dblect/baseline.py +29 -0
- dblect-0.1.0/src/dblect/bootstrap/__init__.py +97 -0
- dblect-0.1.0/src/dblect/bootstrap/skill.md +290 -0
- dblect-0.1.0/src/dblect/check/__init__.py +59 -0
- dblect-0.1.0/src/dblect/check/coverage.py +132 -0
- dblect-0.1.0/src/dblect/check/findings.py +135 -0
- dblect-0.1.0/src/dblect/check/flags.py +79 -0
- dblect-0.1.0/src/dblect/check/incremental.py +122 -0
- dblect-0.1.0/src/dblect/check/run.py +713 -0
- dblect-0.1.0/src/dblect/check/worlds.py +126 -0
- dblect-0.1.0/src/dblect/cli/__init__.py +543 -0
- dblect-0.1.0/src/dblect/contracts/__init__.py +59 -0
- dblect-0.1.0/src/dblect/contracts/ast.py +213 -0
- dblect-0.1.0/src/dblect/contracts/compile.py +374 -0
- dblect-0.1.0/src/dblect/contracts/decorator.py +80 -0
- dblect-0.1.0/src/dblect/contracts/proxy.py +343 -0
- dblect-0.1.0/src/dblect/contracts/stubs.py +91 -0
- dblect-0.1.0/src/dblect/demo/__init__.py +20 -0
- dblect-0.1.0/src/dblect/demo/enums.py +65 -0
- dblect-0.1.0/src/dblect/demo/library.py +21 -0
- dblect-0.1.0/src/dblect/execution/__init__.py +5 -0
- dblect-0.1.0/src/dblect/execution/incremental.py +168 -0
- dblect-0.1.0/src/dblect/execution/project_env.py +69 -0
- dblect-0.1.0/src/dblect/execution/run.py +255 -0
- dblect-0.1.0/src/dblect/flatten/__init__.py +13 -0
- dblect-0.1.0/src/dblect/flatten/detector.py +69 -0
- dblect-0.1.0/src/dblect/lineage/__init__.py +41 -0
- dblect-0.1.0/src/dblect/lineage/builder.py +1123 -0
- dblect-0.1.0/src/dblect/lineage/facts/__init__.py +92 -0
- dblect-0.1.0/src/dblect/lineage/facts/grounding.py +190 -0
- dblect-0.1.0/src/dblect/lineage/facts/lattice.py +71 -0
- dblect-0.1.0/src/dblect/lineage/facts/model.py +170 -0
- dblect-0.1.0/src/dblect/lineage/facts/property.py +338 -0
- dblect-0.1.0/src/dblect/lineage/facts/registry.py +115 -0
- dblect-0.1.0/src/dblect/lineage/graph.py +262 -0
- dblect-0.1.0/src/dblect/lineage/predicate.py +521 -0
- dblect-0.1.0/src/dblect/lineage/properties/__init__.py +84 -0
- dblect-0.1.0/src/dblect/lineage/properties/activation.py +43 -0
- dblect-0.1.0/src/dblect/lineage/properties/aggregation_depth.py +71 -0
- dblect-0.1.0/src/dblect/lineage/properties/array_nonemptiness.py +207 -0
- dblect-0.1.0/src/dblect/lineage/properties/domain_type.py +709 -0
- dblect-0.1.0/src/dblect/lineage/properties/functional_dependency.py +540 -0
- dblect-0.1.0/src/dblect/lineage/properties/nullability.py +644 -0
- dblect-0.1.0/src/dblect/lineage/properties/predicate_flow.py +316 -0
- dblect-0.1.0/src/dblect/lineage/properties/uniqueness.py +1252 -0
- dblect-0.1.0/src/dblect/lineage/properties/where_provenance.py +57 -0
- dblect-0.1.0/src/dblect/lineage/property.py +466 -0
- dblect-0.1.0/src/dblect/lineage/semiring.py +110 -0
- dblect-0.1.0/src/dblect/loader.py +130 -0
- dblect-0.1.0/src/dblect/manifest/__init__.py +42 -0
- dblect-0.1.0/src/dblect/manifest/catalog.py +63 -0
- dblect-0.1.0/src/dblect/manifest/dag.py +155 -0
- dblect-0.1.0/src/dblect/manifest/parse.py +732 -0
- dblect-0.1.0/src/dblect/nullability/__init__.py +23 -0
- dblect-0.1.0/src/dblect/nullability/detector.py +502 -0
- dblect-0.1.0/src/dblect/py.typed +0 -0
- dblect-0.1.0/src/dblect/report.py +492 -0
- dblect-0.1.0/src/dblect/sarif.py +418 -0
- dblect-0.1.0/src/dblect/severity.py +152 -0
- dblect-0.1.0/src/dblect/snapshot/__init__.py +18 -0
- dblect-0.1.0/src/dblect/snapshot/detector.py +142 -0
- dblect-0.1.0/src/dblect/sql/__init__.py +101 -0
- dblect-0.1.0/src/dblect/sql/_sqlglot.py +447 -0
- dblect-0.1.0/src/dblect/sql/aggregates.py +181 -0
- dblect-0.1.0/src/dblect/sql/findings.py +100 -0
- dblect-0.1.0/src/dblect/sql/guards.py +261 -0
- dblect-0.1.0/src/dblect/sql/parse.py +166 -0
- dblect-0.1.0/src/dblect/sql/patterns.py +889 -0
- dblect-0.1.0/src/dblect/sql/vocab.py +240 -0
- dblect-0.1.0/src/dblect/templating.py +83 -0
- dblect-0.1.0/src/dblect/types/__init__.py +106 -0
- dblect-0.1.0/src/dblect/types/bridge.py +732 -0
- dblect-0.1.0/src/dblect/types/contract.py +325 -0
- dblect-0.1.0/src/dblect/types/domain.py +270 -0
- dblect-0.1.0/src/dblect/types/enums.py +28 -0
- dblect-0.1.0/src/dblect/types/errors.py +15 -0
- dblect-0.1.0/src/dblect/types/scalars.py +148 -0
- dblect-0.1.0/src/dblect/uniqueness/__init__.py +24 -0
- dblect-0.1.0/src/dblect/uniqueness/detector.py +1070 -0
- dblect-0.1.0/src/dblect/varinf/__init__.py +52 -0
- dblect-0.1.0/src/dblect/varinf/usage.py +224 -0
- dblect-0.1.0/src/dblect/varinf/walker.py +343 -0
- dblect-0.1.0/tests/__init__.py +0 -0
- dblect-0.1.0/tests/audit/__init__.py +0 -0
- dblect-0.1.0/tests/audit/test_fanout_fd_grounding.py +127 -0
- dblect-0.1.0/tests/audit/test_nullable_key_fd_grounding.py +92 -0
- dblect-0.1.0/tests/audit/test_sourcemap.py +224 -0
- dblect-0.1.0/tests/audit/test_suppress.py +432 -0
- dblect-0.1.0/tests/audit/test_walker.py +564 -0
- dblect-0.1.0/tests/bootstrap/__init__.py +0 -0
- dblect-0.1.0/tests/bootstrap/test_skill_drift.py +35 -0
- dblect-0.1.0/tests/check/__init__.py +0 -0
- dblect-0.1.0/tests/check/conftest.py +16 -0
- dblect-0.1.0/tests/check/test_compilation_coverage.py +105 -0
- dblect-0.1.0/tests/check/test_cross_world.py +88 -0
- dblect-0.1.0/tests/check/test_flags.py +126 -0
- dblect-0.1.0/tests/check/test_incremental_check.py +63 -0
- dblect-0.1.0/tests/check/test_run_check.py +799 -0
- dblect-0.1.0/tests/check/test_world_staging.py +97 -0
- dblect-0.1.0/tests/check/test_worlds.py +254 -0
- dblect-0.1.0/tests/cli/__init__.py +0 -0
- dblect-0.1.0/tests/cli/test_check.py +329 -0
- dblect-0.1.0/tests/cli/test_check_baseline.py +97 -0
- dblect-0.1.0/tests/cli/test_init_and_check.py +294 -0
- dblect-0.1.0/tests/cli/test_setup.py +81 -0
- dblect-0.1.0/tests/conftest.py +95 -0
- dblect-0.1.0/tests/contracts/__init__.py +0 -0
- dblect-0.1.0/tests/contracts/test_compile.py +183 -0
- dblect-0.1.0/tests/contracts/test_contract_capture.py +63 -0
- dblect-0.1.0/tests/contracts/test_pbt_contracts.py +96 -0
- dblect-0.1.0/tests/contracts/test_proxy_builds_ast.py +137 -0
- dblect-0.1.0/tests/contracts/test_stubs.py +99 -0
- dblect-0.1.0/tests/execution/__init__.py +0 -0
- dblect-0.1.0/tests/execution/test_incremental.py +81 -0
- dblect-0.1.0/tests/execution/test_run.py +114 -0
- dblect-0.1.0/tests/fixtures/incremental/dbt_project.yml +5 -0
- dblect-0.1.0/tests/fixtures/incremental/models/inc_stateful.sql +14 -0
- dblect-0.1.0/tests/fixtures/incremental/models/inc_watermark.sql +7 -0
- dblect-0.1.0/tests/fixtures/incremental/seeds/events.csv +3 -0
- dblect-0.1.0/tests/fixtures/incremental/seeds/schema.yml +20 -0
- dblect-0.1.0/tests/fixtures/incremental/seeds/state.csv +4 -0
- dblect-0.1.0/tests/fixtures/jaffle/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/jaffle_bigquery/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/dbt_project.yml +34 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/customers.sql +69 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/docs.md +14 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/orders.sql +56 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/schema.yml +84 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/staging/schema.yml +33 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/staging/stg_customers.sql +22 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/staging/stg_orders.sql +23 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/models/staging/stg_payments.sql +25 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/seeds/raw_customers.csv +101 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/seeds/raw_orders.csv +100 -0
- dblect-0.1.0/tests/fixtures/jaffle_project/seeds/raw_payments.csv +114 -0
- dblect-0.1.0/tests/fixtures/jaffle_snowflake_meta/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/sarif/sarif-2.1.0.schema.json +2882 -0
- dblect-0.1.0/tests/fixtures/scenarios/README.md +86 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/dbt_project.yml +21 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/models/staging/schema.yml +50 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/models/staging/stg_customers.sql +22 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/models/staging/stg_orders.sql +23 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/models/staging/stg_payments.sql +23 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/seeds/raw_customers.csv +101 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/seeds/raw_orders.csv +100 -0
- dblect-0.1.0/tests/fixtures/scenarios/base/seeds/raw_payments.csv +117 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/currency_creep/dblect/__init__.py +0 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/currency_creep/dblect/contracts.py +15 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/currency_creep/expected.yml +9 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/currency_creep/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/currency_creep/overlay/models/marts/order_revenue.sql +8 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/currency_creep/story.md +34 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/order_rollup_sound/dblect/__init__.py +0 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/order_rollup_sound/dblect/contracts.py +14 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/order_rollup_sound/expected.yml +3 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/order_rollup_sound/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/order_rollup_sound/overlay/models/marts/revenue_by_order.sql +9 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/order_rollup_sound/story.md +35 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/revenue_by_customer/dblect/__init__.py +0 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/revenue_by_customer/dblect/contracts.py +15 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/revenue_by_customer/expected.yml +6 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/revenue_by_customer/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/revenue_by_customer/overlay/models/marts/revenue_by_customer.sql +9 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/revenue_by_customer/story.md +39 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/total_daily_revenue/dblect/__init__.py +0 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/total_daily_revenue/dblect/contracts.py +9 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/total_daily_revenue/expected.yml +6 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/total_daily_revenue/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/total_daily_revenue/overlay/models/marts/total_daily_revenue.sql +9 -0
- dblect-0.1.0/tests/fixtures/scenarios/cases/total_daily_revenue/story.md +32 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/dbt_project.yml +20 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/manifest.json +1 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/models/renamed_safe.sql +6 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/models/renamed_unsafe.sql +5 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/models/safe_current.sql +4 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/models/safe_outer_filter.sql +10 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/models/unsafe_join.sql +5 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/seeds/raw_orders.csv +100 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/snapshots/orders_snapshot.sql +11 -0
- dblect-0.1.0/tests/fixtures/snapshot_audit/snapshots/orders_snapshot_renamed.sql +17 -0
- dblect-0.1.0/tests/flatten/__init__.py +0 -0
- dblect-0.1.0/tests/flatten/test_detector.py +145 -0
- dblect-0.1.0/tests/lineage/__init__.py +0 -0
- dblect-0.1.0/tests/lineage/_duckdb_oracle.py +61 -0
- dblect-0.1.0/tests/lineage/_lattice_laws.py +59 -0
- dblect-0.1.0/tests/lineage/conftest.py +24 -0
- dblect-0.1.0/tests/lineage/test_array_nonemptiness_propagation.py +319 -0
- dblect-0.1.0/tests/lineage/test_cardinality_cross_model.py +225 -0
- dblect-0.1.0/tests/lineage/test_conditional_activation.py +424 -0
- dblect-0.1.0/tests/lineage/test_config_facts.py +221 -0
- dblect-0.1.0/tests/lineage/test_demo_aggregation_depth.py +97 -0
- dblect-0.1.0/tests/lineage/test_demo_nullability.py +212 -0
- dblect-0.1.0/tests/lineage/test_domain_type_coherence.py +349 -0
- dblect-0.1.0/tests/lineage/test_domain_type_join_keys.py +84 -0
- dblect-0.1.0/tests/lineage/test_domain_type_lattice.py +210 -0
- dblect-0.1.0/tests/lineage/test_domain_type_outer_join.py +137 -0
- dblect-0.1.0/tests/lineage/test_domain_type_propagation.py +206 -0
- dblect-0.1.0/tests/lineage/test_facts_grounding.py +257 -0
- dblect-0.1.0/tests/lineage/test_facts_lattice.py +113 -0
- dblect-0.1.0/tests/lineage/test_facts_property.py +96 -0
- dblect-0.1.0/tests/lineage/test_facts_registry.py +130 -0
- dblect-0.1.0/tests/lineage/test_fanout_grain.py +49 -0
- dblect-0.1.0/tests/lineage/test_functional_dependency_lattice.py +198 -0
- dblect-0.1.0/tests/lineage/test_functional_dependency_propagation.py +523 -0
- dblect-0.1.0/tests/lineage/test_nested_and_unnest.py +229 -0
- dblect-0.1.0/tests/lineage/test_nullability_activation.py +143 -0
- dblect-0.1.0/tests/lineage/test_nullability_facts.py +303 -0
- dblect-0.1.0/tests/lineage/test_nullability_outer_join.py +179 -0
- dblect-0.1.0/tests/lineage/test_pbt_domain_type_soundness.py +183 -0
- dblect-0.1.0/tests/lineage/test_pbt_functional_dependency_soundness.py +361 -0
- dblect-0.1.0/tests/lineage/test_pbt_lineage.py +686 -0
- dblect-0.1.0/tests/lineage/test_pbt_nullability_monotone.py +154 -0
- dblect-0.1.0/tests/lineage/test_pbt_nullability_soundness.py +160 -0
- dblect-0.1.0/tests/lineage/test_pbt_uniqueness_soundness.py +363 -0
- dblect-0.1.0/tests/lineage/test_predicate_flow.py +253 -0
- dblect-0.1.0/tests/lineage/test_predicate_implication.py +368 -0
- dblect-0.1.0/tests/lineage/test_propagator.py +348 -0
- dblect-0.1.0/tests/lineage/test_reference_resolution.py +63 -0
- dblect-0.1.0/tests/lineage/test_resolution_coverage.py +217 -0
- dblect-0.1.0/tests/lineage/test_schema_accumulation.py +118 -0
- dblect-0.1.0/tests/lineage/test_semiring_laws.py +126 -0
- dblect-0.1.0/tests/lineage/test_shared_tree_resolution.py +120 -0
- dblect-0.1.0/tests/lineage/test_sketch_opacity.py +83 -0
- dblect-0.1.0/tests/lineage/test_surrogate_key_facts.py +180 -0
- dblect-0.1.0/tests/lineage/test_uniqueness_facts.py +364 -0
- dblect-0.1.0/tests/lineage/test_uniqueness_lattice.py +99 -0
- dblect-0.1.0/tests/lineage/test_uniqueness_propagation.py +400 -0
- dblect-0.1.0/tests/lineage/test_where_provenance.py +251 -0
- dblect-0.1.0/tests/manifest/__init__.py +0 -0
- dblect-0.1.0/tests/manifest/test_catalog.py +281 -0
- dblect-0.1.0/tests/manifest/test_compilation_status.py +94 -0
- dblect-0.1.0/tests/manifest/test_dag.py +156 -0
- dblect-0.1.0/tests/manifest/test_model_config.py +75 -0
- dblect-0.1.0/tests/manifest/test_parse.py +184 -0
- dblect-0.1.0/tests/nullability/__init__.py +0 -0
- dblect-0.1.0/tests/nullability/test_detector.py +461 -0
- dblect-0.1.0/tests/scenarios/__init__.py +0 -0
- dblect-0.1.0/tests/scenarios/test_scenarios.py +71 -0
- dblect-0.1.0/tests/snapshot/__init__.py +0 -0
- dblect-0.1.0/tests/snapshot/test_snapshot_temporal_filter.py +170 -0
- dblect-0.1.0/tests/sql/__init__.py +0 -0
- dblect-0.1.0/tests/sql/test_aggregates.py +114 -0
- dblect-0.1.0/tests/sql/test_jaffle.py +73 -0
- dblect-0.1.0/tests/sql/test_parse.py +41 -0
- dblect-0.1.0/tests/sql/test_patterns.py +1079 -0
- dblect-0.1.0/tests/sql/test_pbt_structural_hazards.py +247 -0
- dblect-0.1.0/tests/sql/test_result_statement.py +96 -0
- dblect-0.1.0/tests/sql/test_suppression_hint.py +20 -0
- dblect-0.1.0/tests/sql/test_vocab.py +197 -0
- dblect-0.1.0/tests/test_adapters.py +160 -0
- dblect-0.1.0/tests/test_analysis.py +105 -0
- dblect-0.1.0/tests/test_baseline.py +88 -0
- dblect-0.1.0/tests/test_fail_threshold.py +118 -0
- dblect-0.1.0/tests/test_packaging.py +91 -0
- dblect-0.1.0/tests/test_report.py +363 -0
- dblect-0.1.0/tests/test_sarif.py +332 -0
- dblect-0.1.0/tests/test_smoke.py +17 -0
- dblect-0.1.0/tests/test_templating.py +65 -0
- dblect-0.1.0/tests/types/__init__.py +0 -0
- dblect-0.1.0/tests/types/conftest.py +21 -0
- dblect-0.1.0/tests/types/test_contract_aggregation_e2e.py +140 -0
- dblect-0.1.0/tests/types/test_contract_bridge.py +416 -0
- dblect-0.1.0/tests/types/test_contract_integration.py +137 -0
- dblect-0.1.0/tests/types/test_contract_methods.py +200 -0
- dblect-0.1.0/tests/types/test_contract_registration.py +144 -0
- dblect-0.1.0/tests/types/test_domain_type_declaration.py +286 -0
- dblect-0.1.0/tests/types/test_foreign_key_edges.py +197 -0
- dblect-0.1.0/tests/types/test_loader.py +117 -0
- dblect-0.1.0/tests/types/test_pbt_declaration_equivalence.py +110 -0
- dblect-0.1.0/tests/uniqueness/__init__.py +0 -0
- dblect-0.1.0/tests/uniqueness/test_cross_model_fanout.py +284 -0
- dblect-0.1.0/tests/uniqueness/test_detector.py +816 -0
- dblect-0.1.0/tests/varinf/__init__.py +0 -0
- dblect-0.1.0/tests/varinf/test_op_vocabulary.py +51 -0
- dblect-0.1.0/tests/varinf/test_walker.py +221 -0
- dblect-0.1.0/tests/varinf/test_walker_properties.py +161 -0
- dblect-0.1.0/uv.lock +2668 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
check:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
# Single version to conserve Actions minutes; widen the matrix back to the
|
|
19
|
+
# full supported range (3.11-3.13) when budget allows.
|
|
20
|
+
python-version: ["3.13"]
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- name: Install uv
|
|
26
|
+
uses: astral-sh/setup-uv@v3
|
|
27
|
+
with:
|
|
28
|
+
enable-cache: true
|
|
29
|
+
cache-dependency-glob: "uv.lock"
|
|
30
|
+
|
|
31
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
32
|
+
run: uv python install ${{ matrix.python-version }}
|
|
33
|
+
|
|
34
|
+
- name: Sync dependencies
|
|
35
|
+
run: uv sync --frozen --python ${{ matrix.python-version }}
|
|
36
|
+
|
|
37
|
+
- name: Lint (ruff check)
|
|
38
|
+
run: uv run ruff check
|
|
39
|
+
|
|
40
|
+
- name: Format check (ruff format)
|
|
41
|
+
run: uv run ruff format --check
|
|
42
|
+
|
|
43
|
+
- name: Type-check (pyright strict)
|
|
44
|
+
run: uv run pyright
|
|
45
|
+
|
|
46
|
+
# -n auto parallelises across the runner's cores; loadscope keeps each module on
|
|
47
|
+
# one worker so the module-scoped dbt-compile fixtures still run once apiece.
|
|
48
|
+
# Coverage instrumentation adds ~45% to the test step (measured) and nothing gates
|
|
49
|
+
# on it, so it runs only on the main push that publishes the artifact, keeping the
|
|
50
|
+
# PR feedback path on the uninstrumented run of the same tests.
|
|
51
|
+
- name: Tests (pytest)
|
|
52
|
+
if: github.event_name != 'push'
|
|
53
|
+
run: uv run pytest -n auto --dist loadscope
|
|
54
|
+
|
|
55
|
+
- name: Tests (pytest with coverage)
|
|
56
|
+
if: github.event_name == 'push'
|
|
57
|
+
run: uv run pytest -n auto --dist loadscope --cov --cov-report=term-missing --cov-report=xml
|
|
58
|
+
|
|
59
|
+
- name: Upload coverage artifact
|
|
60
|
+
if: github.event_name == 'push' && matrix.python-version == '3.13'
|
|
61
|
+
uses: actions/upload-artifact@v4
|
|
62
|
+
with:
|
|
63
|
+
name: coverage-xml
|
|
64
|
+
path: coverage.xml
|
|
65
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI when a v* tag is pushed. The wheel and sdist are built on a
|
|
4
|
+
# clean runner from the tagged commit, and upload uses PyPI Trusted Publishing
|
|
5
|
+
# (OIDC): no API token is stored anywhere. The `pypi` environment matches the
|
|
6
|
+
# trusted-publisher registration on PyPI and is the place to add a manual
|
|
7
|
+
# approval gate if one is ever wanted.
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags: ["v*"]
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
publish:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment:
|
|
19
|
+
name: pypi
|
|
20
|
+
url: https://pypi.org/p/dblect
|
|
21
|
+
permissions:
|
|
22
|
+
id-token: write # required for OIDC; this is what authorizes the upload
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v3
|
|
29
|
+
|
|
30
|
+
- name: Build wheel and sdist
|
|
31
|
+
run: uv build
|
|
32
|
+
|
|
33
|
+
- name: Guard tag against the packaged version
|
|
34
|
+
# The tag drives the release, but the wheel's version comes from
|
|
35
|
+
# _version.py via hatchling. If they disagree the upload would ship a
|
|
36
|
+
# surprise, so require the built wheel to carry the tag's version and
|
|
37
|
+
# fail loudly before publishing a mismatch.
|
|
38
|
+
run: |
|
|
39
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
40
|
+
if [ ! -f "dist/dblect-${tag}-py3-none-any.whl" ]; then
|
|
41
|
+
echo "tag ${GITHUB_REF_NAME} has no matching wheel; built:" >&2
|
|
42
|
+
ls dist >&2
|
|
43
|
+
exit 1
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
- name: Publish to PyPI
|
|
47
|
+
run: uv publish --trusted-publishing always
|
dblect-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[codz]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
# /MANIFEST is the setuptools sdist artifact at repo root.
|
|
28
|
+
# Anchored to root so our src/dblect/manifest/ package is not matched
|
|
29
|
+
# case-insensitively on macOS/Windows filesystems.
|
|
30
|
+
/MANIFEST
|
|
31
|
+
|
|
32
|
+
# PyInstaller
|
|
33
|
+
# Usually these files are written by a python script from a template
|
|
34
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
35
|
+
*.manifest
|
|
36
|
+
*.spec
|
|
37
|
+
|
|
38
|
+
# Installer logs
|
|
39
|
+
pip-log.txt
|
|
40
|
+
pip-delete-this-directory.txt
|
|
41
|
+
|
|
42
|
+
# Unit test / coverage reports
|
|
43
|
+
htmlcov/
|
|
44
|
+
.tox/
|
|
45
|
+
.nox/
|
|
46
|
+
.coverage
|
|
47
|
+
.coverage.*
|
|
48
|
+
.cache
|
|
49
|
+
nosetests.xml
|
|
50
|
+
coverage.xml
|
|
51
|
+
*.cover
|
|
52
|
+
*.py.cover
|
|
53
|
+
.hypothesis/
|
|
54
|
+
.pytest_cache/
|
|
55
|
+
cover/
|
|
56
|
+
|
|
57
|
+
# Translations
|
|
58
|
+
*.mo
|
|
59
|
+
*.pot
|
|
60
|
+
|
|
61
|
+
# Django stuff:
|
|
62
|
+
*.log
|
|
63
|
+
local_settings.py
|
|
64
|
+
db.sqlite3
|
|
65
|
+
db.sqlite3-journal
|
|
66
|
+
|
|
67
|
+
# Flask stuff:
|
|
68
|
+
instance/
|
|
69
|
+
.webassets-cache
|
|
70
|
+
|
|
71
|
+
# Scrapy stuff:
|
|
72
|
+
.scrapy
|
|
73
|
+
|
|
74
|
+
# Sphinx documentation
|
|
75
|
+
docs/_build/
|
|
76
|
+
|
|
77
|
+
# PyBuilder
|
|
78
|
+
.pybuilder/
|
|
79
|
+
target/
|
|
80
|
+
|
|
81
|
+
# Jupyter Notebook
|
|
82
|
+
.ipynb_checkpoints
|
|
83
|
+
|
|
84
|
+
# IPython
|
|
85
|
+
profile_default/
|
|
86
|
+
ipython_config.py
|
|
87
|
+
|
|
88
|
+
# pyenv
|
|
89
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
90
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
91
|
+
# .python-version
|
|
92
|
+
|
|
93
|
+
# pipenv
|
|
94
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
95
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
96
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
97
|
+
# install all needed dependencies.
|
|
98
|
+
# Pipfile.lock
|
|
99
|
+
|
|
100
|
+
# UV
|
|
101
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
102
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
103
|
+
# commonly ignored for libraries.
|
|
104
|
+
# uv.lock
|
|
105
|
+
|
|
106
|
+
# poetry
|
|
107
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
108
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
109
|
+
# commonly ignored for libraries.
|
|
110
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
111
|
+
# poetry.lock
|
|
112
|
+
# poetry.toml
|
|
113
|
+
|
|
114
|
+
# pdm
|
|
115
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
116
|
+
# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
|
|
117
|
+
# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
|
|
118
|
+
# pdm.lock
|
|
119
|
+
# pdm.toml
|
|
120
|
+
.pdm-python
|
|
121
|
+
.pdm-build/
|
|
122
|
+
|
|
123
|
+
# pixi
|
|
124
|
+
# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
|
|
125
|
+
# pixi.lock
|
|
126
|
+
# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
|
|
127
|
+
# in the .venv directory. It is recommended not to include this directory in version control.
|
|
128
|
+
.pixi
|
|
129
|
+
|
|
130
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
131
|
+
__pypackages__/
|
|
132
|
+
|
|
133
|
+
# Celery stuff
|
|
134
|
+
celerybeat-schedule
|
|
135
|
+
celerybeat.pid
|
|
136
|
+
|
|
137
|
+
# Redis
|
|
138
|
+
*.rdb
|
|
139
|
+
*.aof
|
|
140
|
+
*.pid
|
|
141
|
+
|
|
142
|
+
# RabbitMQ
|
|
143
|
+
mnesia/
|
|
144
|
+
rabbitmq/
|
|
145
|
+
rabbitmq-data/
|
|
146
|
+
|
|
147
|
+
# ActiveMQ
|
|
148
|
+
activemq-data/
|
|
149
|
+
|
|
150
|
+
# SageMath parsed files
|
|
151
|
+
*.sage.py
|
|
152
|
+
|
|
153
|
+
# Environments
|
|
154
|
+
.env
|
|
155
|
+
.envrc
|
|
156
|
+
.venv
|
|
157
|
+
env/
|
|
158
|
+
venv/
|
|
159
|
+
ENV/
|
|
160
|
+
env.bak/
|
|
161
|
+
venv.bak/
|
|
162
|
+
|
|
163
|
+
# Spyder project settings
|
|
164
|
+
.spyderproject
|
|
165
|
+
.spyproject
|
|
166
|
+
|
|
167
|
+
# Rope project settings
|
|
168
|
+
.ropeproject
|
|
169
|
+
|
|
170
|
+
# mkdocs documentation
|
|
171
|
+
/site
|
|
172
|
+
|
|
173
|
+
# mypy
|
|
174
|
+
.mypy_cache/
|
|
175
|
+
.dmypy.json
|
|
176
|
+
dmypy.json
|
|
177
|
+
|
|
178
|
+
# Pyre type checker
|
|
179
|
+
.pyre/
|
|
180
|
+
|
|
181
|
+
# pytype static type analyzer
|
|
182
|
+
.pytype/
|
|
183
|
+
|
|
184
|
+
# Cython debug symbols
|
|
185
|
+
cython_debug/
|
|
186
|
+
|
|
187
|
+
# PyCharm
|
|
188
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
189
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
190
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
191
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
192
|
+
# .idea/
|
|
193
|
+
|
|
194
|
+
# Abstra
|
|
195
|
+
# Abstra is an AI-powered process automation framework.
|
|
196
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
197
|
+
# Learn more at https://abstra.io/docs
|
|
198
|
+
.abstra/
|
|
199
|
+
|
|
200
|
+
# Visual Studio Code
|
|
201
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
202
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
203
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
204
|
+
# you could uncomment the following to ignore the entire vscode folder
|
|
205
|
+
# .vscode/
|
|
206
|
+
# Temporary file for partial code execution
|
|
207
|
+
tempCodeRunnerFile.py
|
|
208
|
+
|
|
209
|
+
# Ruff stuff:
|
|
210
|
+
.ruff_cache/
|
|
211
|
+
|
|
212
|
+
# PyPI configuration file
|
|
213
|
+
.pypirc
|
|
214
|
+
|
|
215
|
+
# Marimo
|
|
216
|
+
marimo/_static/
|
|
217
|
+
marimo/_lsp/
|
|
218
|
+
__marimo__/
|
|
219
|
+
|
|
220
|
+
# Streamlit
|
|
221
|
+
.streamlit/secrets.toml
|
|
222
|
+
HANDOFF.md
|
|
223
|
+
|
|
224
|
+
# Claude Code local worktrees
|
|
225
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.1.0] - 2026-07-11
|
|
11
|
+
|
|
12
|
+
The first published release: the base-world static analyzer, packaged for
|
|
13
|
+
`pip install dblect`. It reads a compiled dbt manifest, propagates column-level
|
|
14
|
+
facts through the DAG, and reports structural and declaration-level findings the
|
|
15
|
+
user can navigate to and act on. No execution and no LLM are required. The
|
|
16
|
+
runtime half (property-based execution, replay-determinism) and the
|
|
17
|
+
flag/var-world analysis are deferred to later releases.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
**Manifest and SQL ingestion**
|
|
22
|
+
|
|
23
|
+
- Typed dbt manifest ingestion: `Manifest`, `Node`, and a `Dag` with topology,
|
|
24
|
+
parsed from `manifest.json`.
|
|
25
|
+
- Analysis over each model's compiled SQL (not the raw Jinja), parsed once per
|
|
26
|
+
run and shared across detectors via a sqlglot wrapper.
|
|
27
|
+
- `catalog.json` ingestion for seed and source columns, so undocumented DAG
|
|
28
|
+
leaves resolve.
|
|
29
|
+
- Source-Jinja front end that discovers `var` and `env_var` references, and a
|
|
30
|
+
typed view of the manifest's macro registry.
|
|
31
|
+
|
|
32
|
+
**Column-level lineage substrate**
|
|
33
|
+
|
|
34
|
+
- A K-relations / semiring propagator carrying where-provenance through the DAG,
|
|
35
|
+
with CTE and UNION intermediates materialized in the lineage graph.
|
|
36
|
+
- The `lineage.facts` substrate: one property propagator over a shared fact
|
|
37
|
+
representation, with nullability discoverers backed by the manifest.
|
|
38
|
+
- Conditional facts that carry their guarding predicate, a predicate-implication
|
|
39
|
+
engine, and a predicate-flow property that accumulates each relation's row
|
|
40
|
+
filter, so conditional uniqueness, candidate keys, and `NOT NULL` activate
|
|
41
|
+
across relations and at intra-model scopes.
|
|
42
|
+
- Nested-field (STRUCT) lineage with explicit `UNNEST` grain.
|
|
43
|
+
|
|
44
|
+
**Uniqueness analysis**
|
|
45
|
+
|
|
46
|
+
- Uniqueness facts from dbt declarations and from structural proof, an
|
|
47
|
+
ordering-key detector and a join-fan-out detector over substrate keys, and
|
|
48
|
+
propagation of uniqueness through SQL operations and across model
|
|
49
|
+
dependencies.
|
|
50
|
+
- Candidate keys derived through surrogate-hash columns; seeds and snapshots
|
|
51
|
+
spanned as test targets.
|
|
52
|
+
|
|
53
|
+
**Domain-type contracts (the declaration DSL)**
|
|
54
|
+
|
|
55
|
+
- The authoring core: `DomainType`, `ModelContract`, `Field`, and the bridge
|
|
56
|
+
that lowers a contract to substrate facts.
|
|
57
|
+
- A contract and proxy layer: an expression AST, column proxies, the
|
|
58
|
+
`@contract` decorator, and fact constructors.
|
|
59
|
+
- Companion-binding column properties (the currency story), may/must refinement
|
|
60
|
+
for widened magnitudes, and domain-type transfer through joins
|
|
61
|
+
(functional-dependency-through-join, outer-join taint, join-key and fan-out
|
|
62
|
+
signals).
|
|
63
|
+
- A config discoverer that grounds an incremental model's `unique_key`, and
|
|
64
|
+
fact-level flag-world plumbing.
|
|
65
|
+
- Wider scalar field types in the classifier: `float` / `Float` as a magnitude,
|
|
66
|
+
`Timestamp` / `datetime` as inert siblings of `Date`, and a bare integer
|
|
67
|
+
(`int` / `Integer` / `BigInt`) accepted as opaque (inert) under the lenient
|
|
68
|
+
default, the ambiguous case a future strict mode rejects (#73).
|
|
69
|
+
|
|
70
|
+
**Structural hazard detectors**
|
|
71
|
+
|
|
72
|
+
- Structural detectors over every model: outer-join `WHERE` inversion,
|
|
73
|
+
non-determinism, the NULL-group-after-outer-join family (`GROUP BY`, join
|
|
74
|
+
key, `NOT IN`), and snapshot reads missing a temporal filter.
|
|
75
|
+
- Source-line provenance on every finding, and SQLFluff-compatible `-- noqa`
|
|
76
|
+
suppression. A bare `-- noqa` silences every dblect finding on its line; a
|
|
77
|
+
`-- noqa: DBLECT_<KIND>` directive silences one detector, and codes without the
|
|
78
|
+
`DBLECT_` prefix are left for `dbt lint` so one comment can address both tools.
|
|
79
|
+
This replaces the earlier bespoke `-- noqa-fixture:` syntax: dblect no longer owns
|
|
80
|
+
the suppression grammar, so it coexists with dbt Fusion's `dbt lint` rather than
|
|
81
|
+
competing with it. Every suppression is still logged in the report's `suppressed:`
|
|
82
|
+
section.
|
|
83
|
+
|
|
84
|
+
**Cross-world analysis**
|
|
85
|
+
|
|
86
|
+
- One analysis door (`dblect.analysis.analyze`) over both detector families,
|
|
87
|
+
returning every finding under one sealed type so a consumer cannot silently
|
|
88
|
+
drop a family.
|
|
89
|
+
- Incremental-worlds checking: compile a model's full-refresh and steady-state
|
|
90
|
+
forms data-free, run both detector families over each, and difference the
|
|
91
|
+
findings so a hazard living only in the unexercised branch surfaces.
|
|
92
|
+
- Coverage reporting that separates resolution (lineage the propagator could
|
|
93
|
+
follow) from grounding (columns a fact actually checks).
|
|
94
|
+
|
|
95
|
+
**CLI and reporting**
|
|
96
|
+
|
|
97
|
+
- The `dblect` CLI: `check` runs both detector families over a project, `init`
|
|
98
|
+
scaffolds the declaration tree and writes model stubs, `version` prints the
|
|
99
|
+
installed version.
|
|
100
|
+
- Text and JSON reporters under one versioned schema, with a non-zero exit on
|
|
101
|
+
unsuppressed findings and a `--no-fail` override. Status messages go to
|
|
102
|
+
stderr so stdout is a clean report.
|
|
103
|
+
- A validated-adapter gate, with `--dialect` as the operator's opt-in
|
|
104
|
+
acknowledgment that detector behavior is best-effort off the validated set.
|
|
105
|
+
- A library of demo scenarios: developer-introduced bugs that `dblect check`
|
|
106
|
+
catches.
|
|
107
|
+
|
|
108
|
+
**Execution harness**
|
|
109
|
+
|
|
110
|
+
- A DuckDB execution harness that runs dbt models via subprocess with fixture
|
|
111
|
+
overrides, the substrate the runtime layers will build on.
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- `aggregation_not_well_typed` findings now name what the coherence guard
|
|
116
|
+
reasoned about: the aggregate and the column it reduced, the per-row companion
|
|
117
|
+
that is not held constant, and the grouping that fails to hold it, instead of
|
|
118
|
+
a generic message (#109).
|
|
119
|
+
- Aggregate behavior is now a first-class combine/select/count classification
|
|
120
|
+
(`dblect.sql.aggregates`), the single source of truth for both arming the
|
|
121
|
+
coherence guard and the not-well-typed finding. Keying on the sqlglot node type
|
|
122
|
+
covers every dialect at once; `min`/`max` are classified as selecting aggregates
|
|
123
|
+
and widen their result tag to top on a varying companion under the lenient
|
|
124
|
+
default (#115).
|
|
125
|
+
|
|
126
|
+
### Fixed
|
|
127
|
+
|
|
128
|
+
- `count` (and `count_if`, `approx_count_distinct`) over a typed magnitude no
|
|
129
|
+
longer raises a spurious `aggregation_not_well_typed` finding. Conversely,
|
|
130
|
+
`stddev`, `variance`, `kurtosis`, `skewness`, `median`, `mode`, and the
|
|
131
|
+
quantile/percentile family now correctly flag a mixed-currency reduction (#115).
|
|
132
|
+
|
|
133
|
+
[Unreleased]: https://github.com/dvryaboy/dblect/compare/v0.1.0...HEAD
|
|
134
|
+
[0.1.0]: https://github.com/dvryaboy/dblect/releases/tag/v0.1.0
|
dblect-0.1.0/CLAUDE.md
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Code Style
|
|
2
|
+
We avoid stringy typing like the plague.
|
|
3
|
+
We use rigorous types.
|
|
4
|
+
|
|
5
|
+
We reach for an existing helper before writing a parallel one, and generalize it when a second caller needs a variant. A comment or commit message that reaches for "mirroring", "duplicate of", or "acceptable for now" is a signal to stop and reuse the original instead.
|
|
6
|
+
|
|
7
|
+
## Testing
|
|
8
|
+
We test rigorously.
|
|
9
|
+
Whenever applicable, we design quality PBT tests, or otherwise leverage provers and exhaustive testing.
|
|
10
|
+
We do not write tests for tests' sakes. We avoid test theater.
|
|
11
|
+
We prefer to test at the boundaries instead of implementation specifics; tests should survive implementation
|
|
12
|
+
changes that do not change contracts.
|
|
13
|
+
Excessive use of mocking is a smell. Loads of boilerplate is a smell.
|
|
14
|
+
Pin contracts, not coincidences. If fragile cooperation is observed (two functions work correctly together only because of implementation details, not contract guarantees), fix the contracts in code rather than pinning the accidental behavior in a test.
|
|
15
|
+
|
|
16
|
+
We work test-first: the failing test (or the property / lattice-law spec) comes before the implementation, so it pins the intended contract rather than rationalizing whatever the code happens to do. When the contract is amenable, we design the property-based test and its generators first, before the code, so the generator is not shaped to fit the implementation.
|
|
17
|
+
|
|
18
|
+
A whole suite passing on its first run is a smell rather than a win: it usually means the tests are weak or were written after the code. When it happens, we prove the tests bite by injecting deliberate contract violations and confirming a test fails for each.
|
|
19
|
+
|
|
20
|
+
We enumerate the input space rather than sampling it: a condition over a closed type earns a test per value, and the common ones (`COUNT(*)` among the duplicate-sensitive aggregates) belong in that space rather than being deferred as edge cases.
|
|
21
|
+
|
|
22
|
+
## Soundness
|
|
23
|
+
We discharge each predicate or guard to an exact decision procedure before coding it, with a counterexample that marks its edge and a test that pins it. A plausible structural proxy standing in for a precise semantic condition is where the subtle bugs live.
|
|
24
|
+
|
|
25
|
+
A soundness check that branches on a closed type decides every case explicitly, rather than handling one (a `CROSS` join) and letting the rest fall through to the wrong answer.
|
|
26
|
+
|
|
27
|
+
When new code consumes an existing fact or invariant, we re-establish that fact's soundness across its full input space, even when we did not write it. A new consumer can raise the stakes on a latent over-claim and turn it into a wrong answer.
|
|
28
|
+
|
|
29
|
+
## Comments
|
|
30
|
+
We avoid comment bloat. We write comments that explain the why of things, or help understand particularly
|
|
31
|
+
complex bits of code and call attention to footguns.
|
|
32
|
+
|
|
33
|
+
Specific things to keep out of comments, docstrings, and prose:
|
|
34
|
+
- Phase or tier numbering ("Tier 0", "phase 1", etc.). The numbering is arbitrary and reorderable; refer to work streams by name (e.g., "static analyser", "replay-determinism check", "runtime PBT loop").
|
|
35
|
+
- Narrative counts of fixture or repo contents ("5 models + 3 seeds + 20 tests"). They go stale silently; if a number matters, assert it.
|
|
36
|
+
- References to ephemeral planning files such as `HANDOFF.md`. Those are session-to-session notes and should not be cited from code, tests, or docs.
|
|
37
|
+
|
|
38
|
+
## Prose Style
|
|
39
|
+
All narrative documents and comments should avoid em-dashes and "not x. not y. z" phrasing.
|
|
40
|
+
We do not bash older or alternative approaches: we win by being clear and positive, and complimenting them / acknowledging good ideas and influences when appropriate. We are generous with compliments and praise. We put our work in the context of the broader field.
|
|
41
|
+
|
|
42
|
+
# Development
|
|
43
|
+
We run Python and its tooling through `uv` (`uv run python`, `uv run pytest`, `uv run ruff`, `uv run pyright`); a bare `python` is not on PATH. Quick probes go through `uv run python -c "..."` rather than a throwaway file.
|
|
44
|
+
|
|
45
|
+
Before pushing we run the full gate, which CI runs as separate steps: `uv run ruff check`, `uv run ruff format --check`, `uv run pyright`, `uv run pytest`.
|
|
46
|
+
|