data-contract-registry 0.1.1__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.
@@ -0,0 +1,41 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ name: test (py${{ matrix.python-version }})
12
+ runs-on: ubuntu-latest
13
+ strategy:
14
+ fail-fast: false
15
+ matrix:
16
+ python-version: ["3.11", "3.12", "3.13"]
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Set up Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version: ${{ matrix.python-version }}
24
+ cache: pip
25
+
26
+ - name: Install
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ pip install -e ".[dev]"
30
+
31
+ - name: Lint
32
+ run: ruff check src tests
33
+
34
+ - name: Format check
35
+ run: ruff format --check src tests
36
+
37
+ - name: Type check
38
+ run: mypy src
39
+
40
+ - name: Test
41
+ run: pytest -v
@@ -0,0 +1,35 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags: ["v*"]
6
+
7
+ jobs:
8
+ publish:
9
+ name: Build + publish wheel + sdist
10
+ runs-on: ubuntu-latest
11
+ environment:
12
+ name: pypi
13
+ url: https://pypi.org/p/data-contract-registry
14
+ permissions:
15
+ id-token: write
16
+ contents: read
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ with:
20
+ fetch-depth: 0
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.13"
24
+ - name: Install build tools
25
+ run: |
26
+ python -m pip install --upgrade pip
27
+ python -m pip install build
28
+ - name: Build distributions
29
+ run: python -m build
30
+ - name: Inspect distributions
31
+ run: ls -la dist/
32
+ - name: Publish to PyPI
33
+ uses: pypa/gh-action-pypi-publish@release/v1
34
+ with:
35
+ attestations: true
@@ -0,0 +1,12 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
8
+ dist/
9
+ build/
10
+ .coverage
11
+ htmlcov/
12
+ .DS_Store
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miz Causevic / Kinetic Gain
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,246 @@
1
+ Metadata-Version: 2.4
2
+ Name: data-contract-registry
3
+ Version: 0.1.1
4
+ Summary: Schema registry for data contracts: semver versioning, compatibility checks (backward/forward/full), ownership, freshness SLAs. The 'you can't promote it without an approved contract' pattern for data pipelines. Optional audit-stream-py integration via AUDIT_STREAM_URL.
5
+ Project-URL: Homepage, https://github.com/mizcausevic-dev/data-contract-registry
6
+ Project-URL: Repository, https://github.com/mizcausevic-dev/data-contract-registry
7
+ Project-URL: Issues, https://github.com/mizcausevic-dev/data-contract-registry/issues
8
+ Project-URL: Author Site, https://kineticgain.com/
9
+ Author-email: Miz Causevic <miz@kineticgain.com>
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: data-contract,data-quality,fastapi,kinetic-gain,schema-registry
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Framework :: FastAPI
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: Database
23
+ Classifier: Topic :: Software Development :: Libraries
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.11
26
+ Requires-Dist: httpx>=0.27
27
+ Requires-Dist: pydantic>=2.7
28
+ Requires-Dist: pyyaml>=6.0
29
+ Provides-Extra: api
30
+ Requires-Dist: fastapi>=0.115; extra == 'api'
31
+ Requires-Dist: uvicorn[standard]>=0.30; extra == 'api'
32
+ Provides-Extra: dev
33
+ Requires-Dist: fastapi>=0.115; extra == 'dev'
34
+ Requires-Dist: httpx>=0.27; extra == 'dev'
35
+ Requires-Dist: mypy>=1.11; extra == 'dev'
36
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
37
+ Requires-Dist: pytest>=8.2; extra == 'dev'
38
+ Requires-Dist: ruff>=0.6; extra == 'dev'
39
+ Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
40
+ Requires-Dist: uvicorn[standard]>=0.30; extra == 'dev'
41
+ Description-Content-Type: text/markdown
42
+
43
+ # data-contract-registry
44
+
45
+ [![CI](https://github.com/mizcausevic-dev/data-contract-registry/actions/workflows/ci.yml/badge.svg)](https://github.com/mizcausevic-dev/data-contract-registry/actions/workflows/ci.yml)
46
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
48
+
49
+ **Schema registry for data contracts.** Semver versioning, compatibility checks (backward / forward / full), declared owners, freshness SLAs. The "you can't promote a new dataset version without an approved contract" pattern, lifted from API governance and aimed at data pipelines.
50
+
51
+ The headline endpoint is `POST /contracts` — register a new version, get back a deterministic compatibility report or a 422 with every breaking change called out by field name and kind.
52
+
53
+ ---
54
+
55
+ ## Why
56
+
57
+ The thing that gets data teams paged at 2am isn't a missing test. It's a producer who quietly removed `ltv` because "we never use it anymore" while three downstream dashboards still join on it. Schema registries (Confluent, Buf, etc.) solved this for streaming and gRPC; data pipelines need the same hardness in a shape that fits the things data teams actually argue about:
58
+
59
+ - **owners** — who do I page when this dataset goes stale
60
+ - **freshness SLA** — when does "stale" become "broken"
61
+ - **primary key** — changing it is a `MAJOR`, not a `MINOR`
62
+ - **enum drift** — adding a value is fine; removing one is a backward-compatibility break
63
+ - **deprecation policy** — flag a version with the URI of the migration plan; don't delete it
64
+
65
+ This package is the smallest thing that does all of those.
66
+
67
+ ---
68
+
69
+ ## Install
70
+
71
+ ```bash
72
+ pip install data-contract-registry
73
+ # with the FastAPI surface:
74
+ pip install "data-contract-registry[api]"
75
+ ```
76
+
77
+ Python 3.11+. Runtime deps: `pydantic` + `PyYAML`.
78
+
79
+ ---
80
+
81
+ ## Library quickstart
82
+
83
+ ```python
84
+ from data_contract_registry import (
85
+ ContractRegistry,
86
+ DataContract,
87
+ DataField,
88
+ Owner,
89
+ )
90
+
91
+ registry = ContractRegistry()
92
+
93
+ v1 = DataContract(
94
+ dataset_id="users.daily_active",
95
+ version="1.0.0",
96
+ primary_key=["user_id", "active_date"],
97
+ owners=[Owner(team="growth-platform", contact="#growth-platform")],
98
+ fields=[
99
+ DataField(name="user_id", type="string"),
100
+ DataField(name="active_date", type="timestamp"),
101
+ DataField(name="plan", type="string", enum=["free", "pro", "enterprise"]),
102
+ DataField(name="ltv", type="number", required=False),
103
+ ],
104
+ status="active",
105
+ )
106
+ registry.register(v1)
107
+
108
+ # Compatible promotion (added an optional field).
109
+ v1_1 = v1.model_copy(update={
110
+ "version": "1.1.0",
111
+ "fields": [*v1.fields, DataField(name="signup_source", type="string", required=False)],
112
+ })
113
+ report = registry.register(v1_1)
114
+ print(report.compatible) # True
115
+
116
+ # Incompatible promotion — removing a field breaks backward compatibility.
117
+ v2 = v1.model_copy(update={"version": "2.0.0", "fields": [f for f in v1.fields if f.name != "ltv"]})
118
+ report = registry.register(v2)
119
+ print(report.compatible) # False
120
+ print(report.errors[0].kind) # "field_removed"
121
+ print(report.errors[0].message) # "field 'ltv' was removed; old data will fail validation"
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Compatibility modes
127
+
128
+ | Mode | Meaning |
129
+ | ---------- | --- |
130
+ | `backward` | New schema can read data produced by the previous schema. **Default.** Consumers upgrade first. |
131
+ | `forward` | Previous schema can read data produced by the new schema. Producers upgrade first. |
132
+ | `full` | Both. |
133
+ | `none` | Anything goes. First-time onboarding only. |
134
+
135
+ The checks the engine knows how to flag (each carries a structured `kind` so you can build CI gates around specific failures):
136
+
137
+ | Kind | Severity | Mode |
138
+ | -------------------------- | -------- | --- |
139
+ | `field_removed` | error | backward |
140
+ | `field_type_changed` | error | backward |
141
+ | `field_required_added` | error | backward (optional→required) **or** forward (new required field) |
142
+ | `field_enum_shrunk` | error | backward |
143
+ | `primary_key_changed` | error | always |
144
+ | `version_not_increasing` | error | always |
145
+ | `owner_missing` | error | always |
146
+
147
+ ---
148
+
149
+ ## FastAPI surface
150
+
151
+ ```bash
152
+ pip install "data-contract-registry[api]"
153
+ uvicorn data_contract_registry.app:app --port 8090
154
+ ```
155
+
156
+ | Method | Path | What it does |
157
+ | --- | --- | --- |
158
+ | GET | `/` | Service info. |
159
+ | GET | `/healthz` | Liveness probe. |
160
+ | GET | `/datasets` | List registered dataset IDs. |
161
+ | POST | `/contracts` | Register / promote a contract. 422 with a structured issue list when incompatible. |
162
+ | POST | `/contracts/check` | Dry-run compatibility check — does **not** register. |
163
+ | GET | `/contracts/{ds}/latest` | Latest **active** contract for a dataset. |
164
+ | GET | `/contracts/{ds}/versions` | Full version history. |
165
+ | GET | `/contracts/{ds}/versions/{v}` | One specific version. |
166
+ | POST | `/contracts/{ds}/versions/{v}/deprecate` | Mark deprecated with a migration URI. |
167
+ | POST | `/contracts/{ds}/versions/{v}/archive` | Archive a version (history preserved). |
168
+ | POST | `/contracts/owners/from-decision-card` | **Cross-ecosystem hook** — pull owners out of a Decision Card. |
169
+
170
+ Bundles are held in-memory by default. For restart-safe storage, swap `_BundleStore`'s implementation; the protocol is small.
171
+
172
+ ---
173
+
174
+ ## The cross-ecosystem hook
175
+
176
+ The third hook in the portfolio (after `procurement-decision-api` → `policy-as-code-engine` and the Suite → Decision Intelligence bridge). When a buyer approves a vendor whose data product the team will consume, the Decision Card's `buyer.name` + `decision_maker` are **the right answer** to "who owns the contract on our side":
177
+
178
+ ```bash
179
+ curl -X POST http://localhost:8090/contracts/owners/from-decision-card \
180
+ -H 'Content-Type: application/json' \
181
+ -d @decision-card.json
182
+ # -> [
183
+ # {"team": "Springfield USD", "contact": "#data-platform"},
184
+ # {"team": "Director of Data (Alex Chen)", "contact": null}
185
+ # ]
186
+ ```
187
+
188
+ Drop that list straight into `DataContract.owners` and the registration carries paging info the team didn't have to re-type.
189
+
190
+ ---
191
+
192
+ ## YAML authoring
193
+
194
+ ```yaml
195
+ # contracts/users-daily-active.yaml
196
+ dataset_id: users.daily_active
197
+ version: "1.0.0"
198
+ owners:
199
+ - team: growth-platform
200
+ contact: "#growth-platform"
201
+ freshness_sla:
202
+ max_lag_seconds: 86400
203
+ fields:
204
+ - {name: user_id, type: string}
205
+ - {name: active_date, type: timestamp}
206
+ - {name: plan, type: string, enum: [free, pro, enterprise]}
207
+ ```
208
+
209
+ Hand-author in YAML, validate in CI, register from Python:
210
+
211
+ ```python
212
+ import yaml
213
+ from pathlib import Path
214
+ from data_contract_registry import ContractRegistry, DataContract
215
+
216
+ raw = yaml.safe_load(Path("contracts/users-daily-active.yaml").read_text())
217
+ ContractRegistry().register(DataContract.model_validate(raw))
218
+ ```
219
+
220
+ ---
221
+
222
+ ## Tests
223
+
224
+ ```bash
225
+ pip install -e ".[dev]"
226
+ ruff check src tests && ruff format --check src tests
227
+ mypy src
228
+ pytest -v
229
+ ```
230
+
231
+ CI matrix runs Python 3.11 / 3.12 / 3.13.
232
+
233
+ ---
234
+
235
+ ## Related in this ecosystem
236
+
237
+ - **[procurement-decision-api](https://github.com/mizcausevic-dev/procurement-decision-api)** — drafts the Decision Cards this registry pulls owners from.
238
+ - **[policy-as-code-engine](https://github.com/mizcausevic-dev/policy-as-code-engine)** — pair with this registry to enforce contracts at request time.
239
+ - **[slo-budget-tracker](https://github.com/mizcausevic-dev/slo-budget-tracker)** — wire your freshness SLA into the same monitoring story.
240
+ - More at [kineticgain.com](https://kineticgain.com/).
241
+
242
+ ---
243
+
244
+ ## License
245
+
246
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,204 @@
1
+ # data-contract-registry
2
+
3
+ [![CI](https://github.com/mizcausevic-dev/data-contract-registry/actions/workflows/ci.yml/badge.svg)](https://github.com/mizcausevic-dev/data-contract-registry/actions/workflows/ci.yml)
4
+ [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
+
7
+ **Schema registry for data contracts.** Semver versioning, compatibility checks (backward / forward / full), declared owners, freshness SLAs. The "you can't promote a new dataset version without an approved contract" pattern, lifted from API governance and aimed at data pipelines.
8
+
9
+ The headline endpoint is `POST /contracts` — register a new version, get back a deterministic compatibility report or a 422 with every breaking change called out by field name and kind.
10
+
11
+ ---
12
+
13
+ ## Why
14
+
15
+ The thing that gets data teams paged at 2am isn't a missing test. It's a producer who quietly removed `ltv` because "we never use it anymore" while three downstream dashboards still join on it. Schema registries (Confluent, Buf, etc.) solved this for streaming and gRPC; data pipelines need the same hardness in a shape that fits the things data teams actually argue about:
16
+
17
+ - **owners** — who do I page when this dataset goes stale
18
+ - **freshness SLA** — when does "stale" become "broken"
19
+ - **primary key** — changing it is a `MAJOR`, not a `MINOR`
20
+ - **enum drift** — adding a value is fine; removing one is a backward-compatibility break
21
+ - **deprecation policy** — flag a version with the URI of the migration plan; don't delete it
22
+
23
+ This package is the smallest thing that does all of those.
24
+
25
+ ---
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install data-contract-registry
31
+ # with the FastAPI surface:
32
+ pip install "data-contract-registry[api]"
33
+ ```
34
+
35
+ Python 3.11+. Runtime deps: `pydantic` + `PyYAML`.
36
+
37
+ ---
38
+
39
+ ## Library quickstart
40
+
41
+ ```python
42
+ from data_contract_registry import (
43
+ ContractRegistry,
44
+ DataContract,
45
+ DataField,
46
+ Owner,
47
+ )
48
+
49
+ registry = ContractRegistry()
50
+
51
+ v1 = DataContract(
52
+ dataset_id="users.daily_active",
53
+ version="1.0.0",
54
+ primary_key=["user_id", "active_date"],
55
+ owners=[Owner(team="growth-platform", contact="#growth-platform")],
56
+ fields=[
57
+ DataField(name="user_id", type="string"),
58
+ DataField(name="active_date", type="timestamp"),
59
+ DataField(name="plan", type="string", enum=["free", "pro", "enterprise"]),
60
+ DataField(name="ltv", type="number", required=False),
61
+ ],
62
+ status="active",
63
+ )
64
+ registry.register(v1)
65
+
66
+ # Compatible promotion (added an optional field).
67
+ v1_1 = v1.model_copy(update={
68
+ "version": "1.1.0",
69
+ "fields": [*v1.fields, DataField(name="signup_source", type="string", required=False)],
70
+ })
71
+ report = registry.register(v1_1)
72
+ print(report.compatible) # True
73
+
74
+ # Incompatible promotion — removing a field breaks backward compatibility.
75
+ v2 = v1.model_copy(update={"version": "2.0.0", "fields": [f for f in v1.fields if f.name != "ltv"]})
76
+ report = registry.register(v2)
77
+ print(report.compatible) # False
78
+ print(report.errors[0].kind) # "field_removed"
79
+ print(report.errors[0].message) # "field 'ltv' was removed; old data will fail validation"
80
+ ```
81
+
82
+ ---
83
+
84
+ ## Compatibility modes
85
+
86
+ | Mode | Meaning |
87
+ | ---------- | --- |
88
+ | `backward` | New schema can read data produced by the previous schema. **Default.** Consumers upgrade first. |
89
+ | `forward` | Previous schema can read data produced by the new schema. Producers upgrade first. |
90
+ | `full` | Both. |
91
+ | `none` | Anything goes. First-time onboarding only. |
92
+
93
+ The checks the engine knows how to flag (each carries a structured `kind` so you can build CI gates around specific failures):
94
+
95
+ | Kind | Severity | Mode |
96
+ | -------------------------- | -------- | --- |
97
+ | `field_removed` | error | backward |
98
+ | `field_type_changed` | error | backward |
99
+ | `field_required_added` | error | backward (optional→required) **or** forward (new required field) |
100
+ | `field_enum_shrunk` | error | backward |
101
+ | `primary_key_changed` | error | always |
102
+ | `version_not_increasing` | error | always |
103
+ | `owner_missing` | error | always |
104
+
105
+ ---
106
+
107
+ ## FastAPI surface
108
+
109
+ ```bash
110
+ pip install "data-contract-registry[api]"
111
+ uvicorn data_contract_registry.app:app --port 8090
112
+ ```
113
+
114
+ | Method | Path | What it does |
115
+ | --- | --- | --- |
116
+ | GET | `/` | Service info. |
117
+ | GET | `/healthz` | Liveness probe. |
118
+ | GET | `/datasets` | List registered dataset IDs. |
119
+ | POST | `/contracts` | Register / promote a contract. 422 with a structured issue list when incompatible. |
120
+ | POST | `/contracts/check` | Dry-run compatibility check — does **not** register. |
121
+ | GET | `/contracts/{ds}/latest` | Latest **active** contract for a dataset. |
122
+ | GET | `/contracts/{ds}/versions` | Full version history. |
123
+ | GET | `/contracts/{ds}/versions/{v}` | One specific version. |
124
+ | POST | `/contracts/{ds}/versions/{v}/deprecate` | Mark deprecated with a migration URI. |
125
+ | POST | `/contracts/{ds}/versions/{v}/archive` | Archive a version (history preserved). |
126
+ | POST | `/contracts/owners/from-decision-card` | **Cross-ecosystem hook** — pull owners out of a Decision Card. |
127
+
128
+ Bundles are held in-memory by default. For restart-safe storage, swap `_BundleStore`'s implementation; the protocol is small.
129
+
130
+ ---
131
+
132
+ ## The cross-ecosystem hook
133
+
134
+ The third hook in the portfolio (after `procurement-decision-api` → `policy-as-code-engine` and the Suite → Decision Intelligence bridge). When a buyer approves a vendor whose data product the team will consume, the Decision Card's `buyer.name` + `decision_maker` are **the right answer** to "who owns the contract on our side":
135
+
136
+ ```bash
137
+ curl -X POST http://localhost:8090/contracts/owners/from-decision-card \
138
+ -H 'Content-Type: application/json' \
139
+ -d @decision-card.json
140
+ # -> [
141
+ # {"team": "Springfield USD", "contact": "#data-platform"},
142
+ # {"team": "Director of Data (Alex Chen)", "contact": null}
143
+ # ]
144
+ ```
145
+
146
+ Drop that list straight into `DataContract.owners` and the registration carries paging info the team didn't have to re-type.
147
+
148
+ ---
149
+
150
+ ## YAML authoring
151
+
152
+ ```yaml
153
+ # contracts/users-daily-active.yaml
154
+ dataset_id: users.daily_active
155
+ version: "1.0.0"
156
+ owners:
157
+ - team: growth-platform
158
+ contact: "#growth-platform"
159
+ freshness_sla:
160
+ max_lag_seconds: 86400
161
+ fields:
162
+ - {name: user_id, type: string}
163
+ - {name: active_date, type: timestamp}
164
+ - {name: plan, type: string, enum: [free, pro, enterprise]}
165
+ ```
166
+
167
+ Hand-author in YAML, validate in CI, register from Python:
168
+
169
+ ```python
170
+ import yaml
171
+ from pathlib import Path
172
+ from data_contract_registry import ContractRegistry, DataContract
173
+
174
+ raw = yaml.safe_load(Path("contracts/users-daily-active.yaml").read_text())
175
+ ContractRegistry().register(DataContract.model_validate(raw))
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Tests
181
+
182
+ ```bash
183
+ pip install -e ".[dev]"
184
+ ruff check src tests && ruff format --check src tests
185
+ mypy src
186
+ pytest -v
187
+ ```
188
+
189
+ CI matrix runs Python 3.11 / 3.12 / 3.13.
190
+
191
+ ---
192
+
193
+ ## Related in this ecosystem
194
+
195
+ - **[procurement-decision-api](https://github.com/mizcausevic-dev/procurement-decision-api)** — drafts the Decision Cards this registry pulls owners from.
196
+ - **[policy-as-code-engine](https://github.com/mizcausevic-dev/policy-as-code-engine)** — pair with this registry to enforce contracts at request time.
197
+ - **[slo-budget-tracker](https://github.com/mizcausevic-dev/slo-budget-tracker)** — wire your freshness SLA into the same monitoring story.
198
+ - More at [kineticgain.com](https://kineticgain.com/).
199
+
200
+ ---
201
+
202
+ ## License
203
+
204
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,30 @@
1
+ dataset_id: users.daily_active
2
+ version: "1.0.0"
3
+ description: "One row per (user, day) for active users on the consumer plan."
4
+ status: active
5
+ primary_key:
6
+ - user_id
7
+ - active_date
8
+ owners:
9
+ - team: growth-platform
10
+ contact: "#growth-platform"
11
+ freshness_sla:
12
+ max_lag_seconds: 86400 # one day
13
+ measurement: event_time
14
+ fields:
15
+ - name: user_id
16
+ type: string
17
+ description: "Subject identifier — stable across signups."
18
+ - name: active_date
19
+ type: timestamp
20
+ description: "UTC midnight of the day the user was active."
21
+ - name: plan
22
+ type: string
23
+ enum: [free, pro, enterprise]
24
+ - name: country
25
+ type: string
26
+ - name: ltv
27
+ type: number
28
+ required: false
29
+ - name: session_count
30
+ type: integer
@@ -0,0 +1,83 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "data-contract-registry"
7
+ version = "0.1.1"
8
+ description = "Schema registry for data contracts: semver versioning, compatibility checks (backward/forward/full), ownership, freshness SLAs. The 'you can't promote it without an approved contract' pattern for data pipelines. Optional audit-stream-py integration via AUDIT_STREAM_URL."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.11"
12
+ authors = [
13
+ { name = "Miz Causevic", email = "miz@kineticgain.com" },
14
+ ]
15
+ keywords = ["data-contract", "schema-registry", "data-quality", "fastapi", "kinetic-gain"]
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Framework :: FastAPI",
19
+ "Intended Audience :: Developers",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Operating System :: OS Independent",
22
+ "Programming Language :: Python :: 3",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Topic :: Database",
27
+ "Topic :: Software Development :: Libraries",
28
+ "Typing :: Typed",
29
+ ]
30
+ dependencies = [
31
+ "httpx>=0.27",
32
+ "pydantic>=2.7",
33
+ "PyYAML>=6.0",
34
+ ]
35
+
36
+ [project.optional-dependencies]
37
+ api = [
38
+ "fastapi>=0.115",
39
+ "uvicorn[standard]>=0.30",
40
+ ]
41
+ dev = [
42
+ "fastapi>=0.115",
43
+ "uvicorn[standard]>=0.30",
44
+ "httpx>=0.27",
45
+ "pytest>=8.2",
46
+ "pytest-asyncio>=0.23",
47
+ "ruff>=0.6",
48
+ "mypy>=1.11",
49
+ "types-PyYAML>=6.0",
50
+ ]
51
+
52
+ [project.urls]
53
+ Homepage = "https://github.com/mizcausevic-dev/data-contract-registry"
54
+ Repository = "https://github.com/mizcausevic-dev/data-contract-registry"
55
+ Issues = "https://github.com/mizcausevic-dev/data-contract-registry/issues"
56
+ "Author Site" = "https://kineticgain.com/"
57
+
58
+ [tool.hatch.build.targets.wheel]
59
+ packages = ["src/data_contract_registry"]
60
+
61
+ [tool.pytest.ini_options]
62
+ testpaths = ["tests"]
63
+ asyncio_mode = "auto"
64
+ filterwarnings = [
65
+ "ignore::DeprecationWarning:starlette.*",
66
+ "ignore::DeprecationWarning:fastapi.*",
67
+ ]
68
+
69
+ [tool.ruff]
70
+ line-length = 110
71
+ target-version = "py311"
72
+
73
+ [tool.ruff.lint]
74
+ select = ["E", "F", "I", "B", "UP", "RUF"]
75
+ ignore = ["E501"]
76
+
77
+ [tool.mypy]
78
+ python_version = "3.11"
79
+ strict = true
80
+ disallow_untyped_defs = true
81
+ warn_unused_ignores = true
82
+ warn_redundant_casts = true
83
+ no_implicit_optional = true