contractforge-snowflake 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.
Files changed (96) hide show
  1. contractforge_snowflake-0.1.0/.gitignore +18 -0
  2. contractforge_snowflake-0.1.0/CHANGELOG.md +15 -0
  3. contractforge_snowflake-0.1.0/PKG-INFO +210 -0
  4. contractforge_snowflake-0.1.0/README.md +178 -0
  5. contractforge_snowflake-0.1.0/pyproject.toml +47 -0
  6. contractforge_snowflake-0.1.0/src/contractforge_snowflake/__init__.py +68 -0
  7. contractforge_snowflake-0.1.0/src/contractforge_snowflake/access/__init__.py +5 -0
  8. contractforge_snowflake-0.1.0/src/contractforge_snowflake/access/runtime.py +264 -0
  9. contractforge_snowflake-0.1.0/src/contractforge_snowflake/adapter.py +78 -0
  10. contractforge_snowflake-0.1.0/src/contractforge_snowflake/annotations/__init__.py +9 -0
  11. contractforge_snowflake-0.1.0/src/contractforge_snowflake/annotations/runtime.py +258 -0
  12. contractforge_snowflake-0.1.0/src/contractforge_snowflake/api.py +51 -0
  13. contractforge_snowflake-0.1.0/src/contractforge_snowflake/capabilities/__init__.py +17 -0
  14. contractforge_snowflake-0.1.0/src/contractforge_snowflake/capabilities/sql_warehouse.py +46 -0
  15. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/__init__.py +79 -0
  16. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/_helpers.py +132 -0
  17. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/cost.py +52 -0
  18. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/dashboard.py +34 -0
  19. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/lineage.py +47 -0
  20. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/maintenance.py +47 -0
  21. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/plan.py +22 -0
  22. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/project.py +84 -0
  23. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/publish.py +54 -0
  24. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/run.py +24 -0
  25. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cli/smoke.py +112 -0
  26. contractforge_snowflake-0.1.0/src/contractforge_snowflake/connection_options.py +70 -0
  27. contractforge_snowflake-0.1.0/src/contractforge_snowflake/contract_extensions.py +52 -0
  28. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cost/__init__.py +5 -0
  29. contractforge_snowflake-0.1.0/src/contractforge_snowflake/cost/reconciliation.py +210 -0
  30. contractforge_snowflake-0.1.0/src/contractforge_snowflake/dashboards/__init__.py +15 -0
  31. contractforge_snowflake-0.1.0/src/contractforge_snowflake/dashboards/control_tables.py +145 -0
  32. contractforge_snowflake-0.1.0/src/contractforge_snowflake/deployment/__init__.py +15 -0
  33. contractforge_snowflake-0.1.0/src/contractforge_snowflake/deployment/procedure.py +193 -0
  34. contractforge_snowflake-0.1.0/src/contractforge_snowflake/deployment/task_graph.py +211 -0
  35. contractforge_snowflake-0.1.0/src/contractforge_snowflake/diagnostics/__init__.py +13 -0
  36. contractforge_snowflake-0.1.0/src/contractforge_snowflake/diagnostics/portability.py +212 -0
  37. contractforge_snowflake-0.1.0/src/contractforge_snowflake/environment.py +61 -0
  38. contractforge_snowflake-0.1.0/src/contractforge_snowflake/evidence/__init__.py +34 -0
  39. contractforge_snowflake-0.1.0/src/contractforge_snowflake/evidence/ddl.py +84 -0
  40. contractforge_snowflake-0.1.0/src/contractforge_snowflake/evidence/writer.py +877 -0
  41. contractforge_snowflake-0.1.0/src/contractforge_snowflake/lineage/__init__.py +8 -0
  42. contractforge_snowflake-0.1.0/src/contractforge_snowflake/lineage/reconciliation.py +96 -0
  43. contractforge_snowflake-0.1.0/src/contractforge_snowflake/maintenance/__init__.py +15 -0
  44. contractforge_snowflake-0.1.0/src/contractforge_snowflake/maintenance/retention.py +84 -0
  45. contractforge_snowflake-0.1.0/src/contractforge_snowflake/naming/__init__.py +9 -0
  46. contractforge_snowflake-0.1.0/src/contractforge_snowflake/naming/identifiers.py +29 -0
  47. contractforge_snowflake-0.1.0/src/contractforge_snowflake/operations/__init__.py +5 -0
  48. contractforge_snowflake-0.1.0/src/contractforge_snowflake/operations/runtime.py +63 -0
  49. contractforge_snowflake-0.1.0/src/contractforge_snowflake/polling.py +14 -0
  50. contractforge_snowflake-0.1.0/src/contractforge_snowflake/preparation/__init__.py +11 -0
  51. contractforge_snowflake-0.1.0/src/contractforge_snowflake/preparation/registry.py +34 -0
  52. contractforge_snowflake-0.1.0/src/contractforge_snowflake/preparation/sql.py +360 -0
  53. contractforge_snowflake-0.1.0/src/contractforge_snowflake/publish/__init__.py +5 -0
  54. contractforge_snowflake-0.1.0/src/contractforge_snowflake/publish/bundle.py +112 -0
  55. contractforge_snowflake-0.1.0/src/contractforge_snowflake/rendering/__init__.py +5 -0
  56. contractforge_snowflake-0.1.0/src/contractforge_snowflake/rendering/review.py +132 -0
  57. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/__init__.py +54 -0
  58. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/artifacts.py +85 -0
  59. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/execution.py +520 -0
  60. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/project.py +627 -0
  61. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/publish.py +196 -0
  62. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/quality.py +242 -0
  63. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/runner.py +186 -0
  64. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/schema_policy.py +350 -0
  65. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/session.py +177 -0
  66. contractforge_snowflake-0.1.0/src/contractforge_snowflake/runtime/snowpark_handler.py +20 -0
  67. contractforge_snowflake-0.1.0/src/contractforge_snowflake/session_ops.py +60 -0
  68. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/__init__.py +26 -0
  69. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/failure_paths.py +75 -0
  70. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/minimal.py +75 -0
  71. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/models.py +362 -0
  72. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/procedure.py +273 -0
  73. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/runner.py +224 -0
  74. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/stage_publish.py +194 -0
  75. contractforge_snowflake-0.1.0/src/contractforge_snowflake/smoke/task_graph.py +298 -0
  76. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/__init__.py +6 -0
  77. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/models.py +15 -0
  78. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/registry.py +36 -0
  79. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/review.py +23 -0
  80. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/sql.py +23 -0
  81. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/stage_files.py +106 -0
  82. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/table.py +25 -0
  83. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sources/table_refs.py +81 -0
  84. contractforge_snowflake-0.1.0/src/contractforge_snowflake/sql.py +11 -0
  85. contractforge_snowflake-0.1.0/src/contractforge_snowflake/state/__init__.py +23 -0
  86. contractforge_snowflake-0.1.0/src/contractforge_snowflake/state/runtime.py +368 -0
  87. contractforge_snowflake-0.1.0/src/contractforge_snowflake/subtargets.py +27 -0
  88. contractforge_snowflake-0.1.0/src/contractforge_snowflake/values.py +55 -0
  89. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/__init__.py +17 -0
  90. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/append.py +12 -0
  91. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/hash_diff.py +48 -0
  92. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/models.py +41 -0
  93. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/overwrite.py +12 -0
  94. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/registry.py +77 -0
  95. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/upsert.py +32 -0
  96. contractforge_snowflake-0.1.0/src/contractforge_snowflake/write_modes/validation.py +60 -0
@@ -0,0 +1,18 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ .uv-cache/
7
+ .venv/
8
+ .tmp/
9
+ dist/
10
+ build/
11
+ *.egg
12
+ .coverage
13
+ .coverage.*
14
+ htmlcov/
15
+ .DS_Store
16
+ .env
17
+ .env.*
18
+ !.env.example
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to `contractforge-snowflake` are documented in this file.
4
+
5
+ The format follows Keep a Changelog, and this package follows semantic
6
+ versioning as described in `../../docs/specs/api-stability.md`.
7
+
8
+ ## [0.1.0] - 2026-06-08
9
+
10
+ ### Added
11
+
12
+ - Initial public alpha release of the Snowflake adapter.
13
+ - Snowflake SQL warehouse planning, Snowpark library-runner support, staged
14
+ source bindings, deployment artifacts, quality, evidence, lineage, cost and
15
+ project task graph surfaces.
@@ -0,0 +1,210 @@
1
+ Metadata-Version: 2.4
2
+ Name: contractforge-snowflake
3
+ Version: 0.1.0
4
+ Summary: Snowflake adapter for ContractForge Core.
5
+ Project-URL: Homepage, https://github.com/marquesantero/contractforge-core/tree/main/adapters/snowflake
6
+ Project-URL: Documentation, https://marquesantero.github.io/contractforge-core/docs/adapters/snowflake
7
+ Project-URL: Repository, https://github.com/marquesantero/contractforge-core
8
+ Project-URL: Issues, https://github.com/marquesantero/contractforge-core/issues
9
+ Project-URL: Changelog, https://github.com/marquesantero/contractforge-core/blob/main/adapters/snowflake/CHANGELOG.md
10
+ Author: ContractForge contributors
11
+ License: MIT
12
+ Keywords: contractforge,data-contracts,ingestion,snowflake,warehouse
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Libraries
23
+ Requires-Python: >=3.10
24
+ Requires-Dist: contractforge-core<0.2,>=0.1
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8; extra == 'dev'
27
+ Provides-Extra: runtime
28
+ Requires-Dist: snowflake-connector-python>=3; extra == 'runtime'
29
+ Provides-Extra: snowpark
30
+ Requires-Dist: snowflake-snowpark-python>=1; extra == 'snowpark'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ContractForge Snowflake Adapter
34
+
35
+ `contractforge-snowflake` is the Snowflake adapter for ContractForge.
36
+
37
+ The implementation is planning/publish/runtime-first:
38
+
39
+ - consumes ContractForge Core contracts through public core models;
40
+ - declares conservative Snowflake capabilities;
41
+ - builds publish bundles for a stable Snowflake library runner;
42
+ - runs supported contracts through the adapter runtime with Snowflake sessions;
43
+ - writes ContractForge evidence/control tables in Snowflake;
44
+ - renders project task graph deployment artifacts for schedule/timezone and
45
+ step dependencies;
46
+ - keeps Snowflake connector and Snowpark dependencies optional.
47
+
48
+ ```bash
49
+ pip install contractforge-core contractforge-snowflake
50
+ ```
51
+
52
+ ```python
53
+ from contractforge_snowflake import plan_snowflake_contract
54
+
55
+ result = plan_snowflake_contract(contract)
56
+ print(result.status)
57
+ ```
58
+
59
+ ```python
60
+ from contractforge_snowflake import build_snowflake_publish_bundle
61
+
62
+ bundle = build_snowflake_publish_bundle(contract)
63
+ print(bundle.artifacts["snowflake.publish_manifest.json"])
64
+ ```
65
+
66
+ ```python
67
+ from contractforge_snowflake import run_snowflake_contract
68
+
69
+ result = run_snowflake_contract(
70
+ contract_uri="@CONTRACTFORGE_ARTIFACTS/dev/runtime/ANALYTICS.BRONZE_CUSTOMERS.contract.json",
71
+ environment_uri="@CONTRACTFORGE_ARTIFACTS/dev/runtime/ANALYTICS.BRONZE_CUSTOMERS.environment.json",
72
+ session=snowflake_session,
73
+ )
74
+ print(result["status"])
75
+ ```
76
+
77
+ The adapter does not generate per-contract ingestion SQL as the default
78
+ execution model. Published contracts are consumed by the `contractforge_snowflake`
79
+ runtime library. Project deployment artifacts may create Snowflake tasks, but
80
+ those tasks only call the stable runner with contract and environment artifact
81
+ URIs.
82
+
83
+ Governance annotations support table and column comments, plus Snowflake tags
84
+ when tag objects are already provisioned. Use fully qualified tag names such as
85
+ `GOVERNANCE.PUBLIC.DOMAIN` for deterministic live apply. To validate tag intent
86
+ and record `ctrl_ingestion_annotations` evidence without executing tag DDL, set:
87
+
88
+ ```yaml
89
+ extensions:
90
+ snowflake:
91
+ annotation_tag_mode: validate_only
92
+ ```
93
+
94
+ Access governance supports table grants, row access policy attachments and
95
+ masking policy attachments, with each step recorded in
96
+ `ctrl_ingestion_access`. Use `access.mode: validate_only` to verify grant and
97
+ policy intent without applying DDL. `revoke_unmanaged: true` remains
98
+ review-required because it can remove inherited or unmanaged Snowflake access.
99
+
100
+ Use `contractforge-snowflake smoke-stage-publish --execute --execute-cleanup`
101
+ to live-test stage publication. The smoke creates a temporary internal stage,
102
+ uploads the publish bundle, reloads the staged manifest and runtime artifacts,
103
+ and runs the contract through the connector-backed library runner.
104
+
105
+ Staged-file sources support CSV, JSON and Parquet batch reads from Snowflake
106
+ stages. Provide a named Snowflake file format through
107
+ `source.options.file_format`, or use a stage with a default file format. CSV
108
+ sources can project positional fields with `source.options.columns` as either a
109
+ list of names or a mapping of output names to Snowflake expressions. JSON and
110
+ Parquet sources default to a `payload` `VARIANT` projection and can project
111
+ typed columns with expressions such as `$1:order_id::NUMBER`.
112
+
113
+ ```yaml
114
+ source:
115
+ type: staged_files
116
+ path: '@RAW_STAGE/orders/orders.csv'
117
+ format: csv
118
+ options:
119
+ file_format: RAW_CSV_FORMAT
120
+ columns:
121
+ order_id: '$1::NUMBER'
122
+ status: '$2::STRING'
123
+ amount: '$3::NUMBER(10,2)'
124
+ ```
125
+
126
+ Use `contractforge-snowflake smoke-procedure --execute --execute-cleanup` to
127
+ deploy and call the stable Snowpark runtime procedure. The service role needs
128
+ `CREATE PROCEDURE` on the target schema. The smoke accepts the built core and
129
+ adapter wheels locally, stages Snowflake-compatible ZIP copies, and imports
130
+ those ZIP archives from the procedure:
131
+
132
+ ```sql
133
+ GRANT CREATE PROCEDURE ON SCHEMA CONTRACTFORGE_TEST_DB.PUBLIC
134
+ TO ROLE CONTRACTFORGE_INGEST_ROLE;
135
+ ```
136
+
137
+ Use `contractforge-snowflake smoke-task-graph --execute --execute-cleanup` to
138
+ deploy and manually execute a two-step task graph. In addition to the procedure
139
+ grant, the service role needs task creation/execution privileges in the task
140
+ schema, for example:
141
+
142
+ ```sql
143
+ GRANT CREATE TASK ON SCHEMA CONTRACTFORGE_TEST_DB.PUBLIC
144
+ TO ROLE CONTRACTFORGE_INGEST_ROLE;
145
+ GRANT EXECUTE TASK ON ACCOUNT TO ROLE CONTRACTFORGE_INGEST_ROLE;
146
+ ```
147
+
148
+ Project CLI parity includes `deploy-project`, `run-project`, and
149
+ `cleanup-plan`. `run-project --dry-run` renders the root task `EXECUTE TASK`
150
+ commands without connecting; `run-project --wait` polls bounded
151
+ `INFORMATION_SCHEMA.TASK_HISTORY` for terminal task states. `cleanup-plan`
152
+ prints explicit drop commands for tasks and the runtime procedure but does not
153
+ drop data target tables or staged artifacts.
154
+
155
+ Each runtime run records immediate lineage and explain evidence. The lineage
156
+ row in `ctrl_ingestion_lineage` includes a ContractForge/OpenLineage-style
157
+ event with the run id, source reference, target table, row count and Snowflake
158
+ query ids. The explain row in `ctrl_ingestion_explain` captures
159
+ `EXPLAIN USING TEXT` output for the rendered write statement. Disable plan
160
+ capture for a contract with:
161
+
162
+ ```yaml
163
+ extensions:
164
+ snowflake:
165
+ explain_enabled: false
166
+ ```
167
+
168
+ Native Snowflake lineage from `SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY` is
169
+ handled as delayed reconciliation because Account Usage can lag runtime
170
+ execution. The helper probes by structured `QUERY_TAG` `run_id` and returns
171
+ `PENDING` without inserting rows until matching Access History rows are visible.
172
+
173
+ ```bash
174
+ contractforge-snowflake reconcile-lineage \
175
+ --connect-options .tmp/snowflake-smoke/connect-options.yaml \
176
+ --environment .tmp/snowflake-smoke/environment.json \
177
+ --run-id "<contractforge-run-id>"
178
+ ```
179
+
180
+ Use `contractforge-snowflake reconcile-cost` after a run to append delayed
181
+ `ctrl_ingestion_cost` signals from Snowflake Account Usage. The command probes
182
+ `QUERY_HISTORY` by structured `QUERY_TAG` `run_id`; if rows have not arrived
183
+ yet or the service role cannot read Account Usage, it returns `PENDING` without
184
+ inserting duplicate evidence and includes a warning when access is unavailable.
185
+ When rows are available, it deletes prior adapter-owned cost signals for the
186
+ same `run_id`/target table before inserting query-history and, when accessible,
187
+ query-attribution signals.
188
+
189
+ To let the same service role query Account Usage, grant the Snowflake database
190
+ role that covers `QUERY_HISTORY` and `QUERY_ATTRIBUTION_HISTORY`:
191
+
192
+ ```sql
193
+ GRANT DATABASE ROLE SNOWFLAKE.GOVERNANCE_VIEWER
194
+ TO ROLE CONTRACTFORGE_INGEST_ROLE;
195
+ ```
196
+
197
+ ```bash
198
+ contractforge-snowflake reconcile-cost \
199
+ --connect-options .tmp/snowflake-smoke/connect-options.yaml \
200
+ --environment .tmp/snowflake-smoke/environment.json \
201
+ --run-id "<contractforge-run-id>" \
202
+ --target-table '"CONTRACTFORGE_TEST_DB"."PUBLIC"."CF_SMOKE_APPEND_TARGET"' \
203
+ --wait --max-wait-seconds 300
204
+ ```
205
+
206
+ Architecture and implementation plan:
207
+
208
+ - `docs/specs/snowflake-capability-parity.md`
209
+ - `docs/specs/snowflake-adapter-implementation-plan.md`
210
+ - `docs/specs/snowflake-adapter-parity-execution-plan.md`
@@ -0,0 +1,178 @@
1
+ # ContractForge Snowflake Adapter
2
+
3
+ `contractforge-snowflake` is the Snowflake adapter for ContractForge.
4
+
5
+ The implementation is planning/publish/runtime-first:
6
+
7
+ - consumes ContractForge Core contracts through public core models;
8
+ - declares conservative Snowflake capabilities;
9
+ - builds publish bundles for a stable Snowflake library runner;
10
+ - runs supported contracts through the adapter runtime with Snowflake sessions;
11
+ - writes ContractForge evidence/control tables in Snowflake;
12
+ - renders project task graph deployment artifacts for schedule/timezone and
13
+ step dependencies;
14
+ - keeps Snowflake connector and Snowpark dependencies optional.
15
+
16
+ ```bash
17
+ pip install contractforge-core contractforge-snowflake
18
+ ```
19
+
20
+ ```python
21
+ from contractforge_snowflake import plan_snowflake_contract
22
+
23
+ result = plan_snowflake_contract(contract)
24
+ print(result.status)
25
+ ```
26
+
27
+ ```python
28
+ from contractforge_snowflake import build_snowflake_publish_bundle
29
+
30
+ bundle = build_snowflake_publish_bundle(contract)
31
+ print(bundle.artifacts["snowflake.publish_manifest.json"])
32
+ ```
33
+
34
+ ```python
35
+ from contractforge_snowflake import run_snowflake_contract
36
+
37
+ result = run_snowflake_contract(
38
+ contract_uri="@CONTRACTFORGE_ARTIFACTS/dev/runtime/ANALYTICS.BRONZE_CUSTOMERS.contract.json",
39
+ environment_uri="@CONTRACTFORGE_ARTIFACTS/dev/runtime/ANALYTICS.BRONZE_CUSTOMERS.environment.json",
40
+ session=snowflake_session,
41
+ )
42
+ print(result["status"])
43
+ ```
44
+
45
+ The adapter does not generate per-contract ingestion SQL as the default
46
+ execution model. Published contracts are consumed by the `contractforge_snowflake`
47
+ runtime library. Project deployment artifacts may create Snowflake tasks, but
48
+ those tasks only call the stable runner with contract and environment artifact
49
+ URIs.
50
+
51
+ Governance annotations support table and column comments, plus Snowflake tags
52
+ when tag objects are already provisioned. Use fully qualified tag names such as
53
+ `GOVERNANCE.PUBLIC.DOMAIN` for deterministic live apply. To validate tag intent
54
+ and record `ctrl_ingestion_annotations` evidence without executing tag DDL, set:
55
+
56
+ ```yaml
57
+ extensions:
58
+ snowflake:
59
+ annotation_tag_mode: validate_only
60
+ ```
61
+
62
+ Access governance supports table grants, row access policy attachments and
63
+ masking policy attachments, with each step recorded in
64
+ `ctrl_ingestion_access`. Use `access.mode: validate_only` to verify grant and
65
+ policy intent without applying DDL. `revoke_unmanaged: true` remains
66
+ review-required because it can remove inherited or unmanaged Snowflake access.
67
+
68
+ Use `contractforge-snowflake smoke-stage-publish --execute --execute-cleanup`
69
+ to live-test stage publication. The smoke creates a temporary internal stage,
70
+ uploads the publish bundle, reloads the staged manifest and runtime artifacts,
71
+ and runs the contract through the connector-backed library runner.
72
+
73
+ Staged-file sources support CSV, JSON and Parquet batch reads from Snowflake
74
+ stages. Provide a named Snowflake file format through
75
+ `source.options.file_format`, or use a stage with a default file format. CSV
76
+ sources can project positional fields with `source.options.columns` as either a
77
+ list of names or a mapping of output names to Snowflake expressions. JSON and
78
+ Parquet sources default to a `payload` `VARIANT` projection and can project
79
+ typed columns with expressions such as `$1:order_id::NUMBER`.
80
+
81
+ ```yaml
82
+ source:
83
+ type: staged_files
84
+ path: '@RAW_STAGE/orders/orders.csv'
85
+ format: csv
86
+ options:
87
+ file_format: RAW_CSV_FORMAT
88
+ columns:
89
+ order_id: '$1::NUMBER'
90
+ status: '$2::STRING'
91
+ amount: '$3::NUMBER(10,2)'
92
+ ```
93
+
94
+ Use `contractforge-snowflake smoke-procedure --execute --execute-cleanup` to
95
+ deploy and call the stable Snowpark runtime procedure. The service role needs
96
+ `CREATE PROCEDURE` on the target schema. The smoke accepts the built core and
97
+ adapter wheels locally, stages Snowflake-compatible ZIP copies, and imports
98
+ those ZIP archives from the procedure:
99
+
100
+ ```sql
101
+ GRANT CREATE PROCEDURE ON SCHEMA CONTRACTFORGE_TEST_DB.PUBLIC
102
+ TO ROLE CONTRACTFORGE_INGEST_ROLE;
103
+ ```
104
+
105
+ Use `contractforge-snowflake smoke-task-graph --execute --execute-cleanup` to
106
+ deploy and manually execute a two-step task graph. In addition to the procedure
107
+ grant, the service role needs task creation/execution privileges in the task
108
+ schema, for example:
109
+
110
+ ```sql
111
+ GRANT CREATE TASK ON SCHEMA CONTRACTFORGE_TEST_DB.PUBLIC
112
+ TO ROLE CONTRACTFORGE_INGEST_ROLE;
113
+ GRANT EXECUTE TASK ON ACCOUNT TO ROLE CONTRACTFORGE_INGEST_ROLE;
114
+ ```
115
+
116
+ Project CLI parity includes `deploy-project`, `run-project`, and
117
+ `cleanup-plan`. `run-project --dry-run` renders the root task `EXECUTE TASK`
118
+ commands without connecting; `run-project --wait` polls bounded
119
+ `INFORMATION_SCHEMA.TASK_HISTORY` for terminal task states. `cleanup-plan`
120
+ prints explicit drop commands for tasks and the runtime procedure but does not
121
+ drop data target tables or staged artifacts.
122
+
123
+ Each runtime run records immediate lineage and explain evidence. The lineage
124
+ row in `ctrl_ingestion_lineage` includes a ContractForge/OpenLineage-style
125
+ event with the run id, source reference, target table, row count and Snowflake
126
+ query ids. The explain row in `ctrl_ingestion_explain` captures
127
+ `EXPLAIN USING TEXT` output for the rendered write statement. Disable plan
128
+ capture for a contract with:
129
+
130
+ ```yaml
131
+ extensions:
132
+ snowflake:
133
+ explain_enabled: false
134
+ ```
135
+
136
+ Native Snowflake lineage from `SNOWFLAKE.ACCOUNT_USAGE.ACCESS_HISTORY` is
137
+ handled as delayed reconciliation because Account Usage can lag runtime
138
+ execution. The helper probes by structured `QUERY_TAG` `run_id` and returns
139
+ `PENDING` without inserting rows until matching Access History rows are visible.
140
+
141
+ ```bash
142
+ contractforge-snowflake reconcile-lineage \
143
+ --connect-options .tmp/snowflake-smoke/connect-options.yaml \
144
+ --environment .tmp/snowflake-smoke/environment.json \
145
+ --run-id "<contractforge-run-id>"
146
+ ```
147
+
148
+ Use `contractforge-snowflake reconcile-cost` after a run to append delayed
149
+ `ctrl_ingestion_cost` signals from Snowflake Account Usage. The command probes
150
+ `QUERY_HISTORY` by structured `QUERY_TAG` `run_id`; if rows have not arrived
151
+ yet or the service role cannot read Account Usage, it returns `PENDING` without
152
+ inserting duplicate evidence and includes a warning when access is unavailable.
153
+ When rows are available, it deletes prior adapter-owned cost signals for the
154
+ same `run_id`/target table before inserting query-history and, when accessible,
155
+ query-attribution signals.
156
+
157
+ To let the same service role query Account Usage, grant the Snowflake database
158
+ role that covers `QUERY_HISTORY` and `QUERY_ATTRIBUTION_HISTORY`:
159
+
160
+ ```sql
161
+ GRANT DATABASE ROLE SNOWFLAKE.GOVERNANCE_VIEWER
162
+ TO ROLE CONTRACTFORGE_INGEST_ROLE;
163
+ ```
164
+
165
+ ```bash
166
+ contractforge-snowflake reconcile-cost \
167
+ --connect-options .tmp/snowflake-smoke/connect-options.yaml \
168
+ --environment .tmp/snowflake-smoke/environment.json \
169
+ --run-id "<contractforge-run-id>" \
170
+ --target-table '"CONTRACTFORGE_TEST_DB"."PUBLIC"."CF_SMOKE_APPEND_TARGET"' \
171
+ --wait --max-wait-seconds 300
172
+ ```
173
+
174
+ Architecture and implementation plan:
175
+
176
+ - `docs/specs/snowflake-capability-parity.md`
177
+ - `docs/specs/snowflake-adapter-implementation-plan.md`
178
+ - `docs/specs/snowflake-adapter-parity-execution-plan.md`
@@ -0,0 +1,47 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "contractforge-snowflake"
7
+ version = "0.1.0"
8
+ description = "Snowflake adapter for ContractForge Core."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "ContractForge contributors" }]
13
+ keywords = ["contractforge", "snowflake", "data-contracts", "ingestion", "warehouse"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Database",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+ dependencies = ["contractforge-core>=0.1,<0.2"]
27
+
28
+ [project.urls]
29
+ Homepage = "https://github.com/marquesantero/contractforge-core/tree/main/adapters/snowflake"
30
+ Documentation = "https://marquesantero.github.io/contractforge-core/docs/adapters/snowflake"
31
+ Repository = "https://github.com/marquesantero/contractforge-core"
32
+ Issues = "https://github.com/marquesantero/contractforge-core/issues"
33
+ Changelog = "https://github.com/marquesantero/contractforge-core/blob/main/adapters/snowflake/CHANGELOG.md"
34
+
35
+ [project.optional-dependencies]
36
+ dev = ["pytest>=8"]
37
+ runtime = ["snowflake-connector-python>=3"]
38
+ snowpark = ["snowflake-snowpark-python>=1"]
39
+
40
+ [project.scripts]
41
+ contractforge-snowflake = "contractforge_snowflake.cli:main"
42
+
43
+ [tool.hatch.build.targets.wheel]
44
+ packages = ["src/contractforge_snowflake"]
45
+
46
+ [tool.hatch.build.targets.sdist]
47
+ include = ["/src/contractforge_snowflake", "/README.md", "/CHANGELOG.md", "/pyproject.toml"]
@@ -0,0 +1,68 @@
1
+ """Public API for the ContractForge Snowflake adapter."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from importlib.metadata import PackageNotFoundError
6
+ from importlib.metadata import version as _version
7
+
8
+ from contractforge_snowflake.adapter import SnowflakeAdapter
9
+ from contractforge_snowflake.api import (
10
+ build_snowflake_publish_bundle,
11
+ plan_snowflake_contract,
12
+ render_snowflake_contract,
13
+ )
14
+ from contractforge_snowflake.capabilities import (
15
+ SNOWFLAKE_SUBTARGET_SNOWPIPE,
16
+ SNOWFLAKE_SUBTARGET_SQL_WAREHOUSE,
17
+ SNOWFLAKE_SUBTARGET_STREAMS_TASKS,
18
+ SNOWFLAKE_SUBTARGET_TASK_GRAPH,
19
+ snowflake_sql_warehouse_capabilities,
20
+ )
21
+ from contractforge_snowflake.dashboards import render_control_dashboard_artifacts, render_control_dashboard_sql
22
+ from contractforge_snowflake.environment import SnowflakeEnvironment
23
+ from contractforge_snowflake.maintenance import build_control_retention_plan, execute_control_retention_plan
24
+ from contractforge_snowflake.runtime import (
25
+ SnowflakeAccessHistoryLineageResult,
26
+ build_snowflake_project_cleanup_plan,
27
+ deploy_snowflake_project,
28
+ publish_snowflake_contract,
29
+ reconcile_snowflake_access_history_lineage,
30
+ reconcile_snowflake_cost_evidence,
31
+ run_snowflake_contract,
32
+ run_snowflake_project,
33
+ wait_snowflake_project_tasks,
34
+ )
35
+ from contractforge_snowflake.subtargets import list_snowflake_subtargets
36
+
37
+ try:
38
+ __version__ = _version("contractforge-snowflake")
39
+ except PackageNotFoundError: # pragma: no cover - editable/source tree without installed metadata
40
+ __version__ = "0.1.0"
41
+
42
+ __all__ = [
43
+ "SNOWFLAKE_SUBTARGET_SNOWPIPE",
44
+ "SNOWFLAKE_SUBTARGET_SQL_WAREHOUSE",
45
+ "SNOWFLAKE_SUBTARGET_STREAMS_TASKS",
46
+ "SNOWFLAKE_SUBTARGET_TASK_GRAPH",
47
+ "SnowflakeAdapter",
48
+ "SnowflakeAccessHistoryLineageResult",
49
+ "SnowflakeEnvironment",
50
+ "__version__",
51
+ "build_snowflake_publish_bundle",
52
+ "build_control_retention_plan",
53
+ "build_snowflake_project_cleanup_plan",
54
+ "deploy_snowflake_project",
55
+ "execute_control_retention_plan",
56
+ "list_snowflake_subtargets",
57
+ "plan_snowflake_contract",
58
+ "publish_snowflake_contract",
59
+ "reconcile_snowflake_access_history_lineage",
60
+ "reconcile_snowflake_cost_evidence",
61
+ "render_control_dashboard_artifacts",
62
+ "render_control_dashboard_sql",
63
+ "render_snowflake_contract",
64
+ "run_snowflake_contract",
65
+ "run_snowflake_project",
66
+ "snowflake_sql_warehouse_capabilities",
67
+ "wait_snowflake_project_tasks",
68
+ ]
@@ -0,0 +1,5 @@
1
+ """Snowflake access planning and runtime application."""
2
+
3
+ from contractforge_snowflake.access.runtime import SnowflakeAccessResult, access_steps, apply_snowflake_access
4
+
5
+ __all__ = ["SnowflakeAccessResult", "access_steps", "apply_snowflake_access"]