wraith-modelgen 0.4.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 (38) hide show
  1. wraith_modelgen-0.4.0/.claude/settings.local.json +7 -0
  2. wraith_modelgen-0.4.0/.cz.yaml +67 -0
  3. wraith_modelgen-0.4.0/.gitea/CODEOWNERS.md +5 -0
  4. wraith_modelgen-0.4.0/.gitea/PULL_REQUEST_TEMPLATE.md +42 -0
  5. wraith_modelgen-0.4.0/.gitea/workflows/pages.yml +64 -0
  6. wraith_modelgen-0.4.0/.gitea/workflows/release.yml +23 -0
  7. wraith_modelgen-0.4.0/.gitea/workflows/test.yml +31 -0
  8. wraith_modelgen-0.4.0/.gitignore +167 -0
  9. wraith_modelgen-0.4.0/.pre-commit-config.yaml +68 -0
  10. wraith_modelgen-0.4.0/.secrets.baseline +127 -0
  11. wraith_modelgen-0.4.0/CHANGELOG.md +41 -0
  12. wraith_modelgen-0.4.0/CONTRIBUTING.md +59 -0
  13. wraith_modelgen-0.4.0/LICENSE +21 -0
  14. wraith_modelgen-0.4.0/PKG-INFO +220 -0
  15. wraith_modelgen-0.4.0/README.md +157 -0
  16. wraith_modelgen-0.4.0/docs/architecture.md +39 -0
  17. wraith_modelgen-0.4.0/docs/coverage.svg +1 -0
  18. wraith_modelgen-0.4.0/docs/index.md +50 -0
  19. wraith_modelgen-0.4.0/docs/reference.md +75 -0
  20. wraith_modelgen-0.4.0/docs/security.md +23 -0
  21. wraith_modelgen-0.4.0/docs/usage.md +57 -0
  22. wraith_modelgen-0.4.0/example_contract.yml +92 -0
  23. wraith_modelgen-0.4.0/mkdocs.yml +37 -0
  24. wraith_modelgen-0.4.0/pyproject.toml +109 -0
  25. wraith_modelgen-0.4.0/src/modelgen/__init__.py +1 -0
  26. wraith_modelgen-0.4.0/src/modelgen/cli.py +373 -0
  27. wraith_modelgen-0.4.0/src/modelgen/config.py +62 -0
  28. wraith_modelgen-0.4.0/src/modelgen/contract.py +265 -0
  29. wraith_modelgen-0.4.0/src/modelgen/generator.py +363 -0
  30. wraith_modelgen-0.4.0/src/modelgen/introspect.py +134 -0
  31. wraith_modelgen-0.4.0/src/templates/raw.sql.j2 +42 -0
  32. wraith_modelgen-0.4.0/src/templates/raw_schema.yml.j2 +35 -0
  33. wraith_modelgen-0.4.0/src/templates/staging.sql.j2 +28 -0
  34. wraith_modelgen-0.4.0/src/templates/staging_schema.yml.j2 +32 -0
  35. wraith_modelgen-0.4.0/tasks.py +47 -0
  36. wraith_modelgen-0.4.0/tests/test_cli.py +376 -0
  37. wraith_modelgen-0.4.0/tests/test_modelgen.py +667 -0
  38. wraith_modelgen-0.4.0/uv.lock +1851 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(uv run *)"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,67 @@
1
+ commitizen:
2
+ name: cz_customize
3
+ version_provider: uv
4
+ version_scheme: semver
5
+ major_version_zero: true
6
+ update_changelog_on_bump: true
7
+ tag_format: v$version
8
+ bump_message: ":ghost: chore: release v$current_version -> v$new_version [skip ci]"
9
+
10
+ customize:
11
+ bump_map:
12
+ Break: MAJOR
13
+ break: MAJOR
14
+ Feature: MINOR
15
+ feature: MINOR
16
+ Fix: PATCH
17
+ fix: PATCH
18
+ Bug: PATCH
19
+ bug: PATCH
20
+
21
+ # Removed '^' to allow emojis/prefixes to exist without breaking the match
22
+ bump_pattern: '(feature|fix|bug|break|Feature|Fix|Bug|Break)/[A-Z]{3}-\d{5}'
23
+
24
+ # Parser: Extracts the type for the changelog even if there's an emoji before it
25
+ commit_parser: '(?P<change_type>feature|fix|bug|break|Feature|Fix|Bug|Break)/[A-Z]{3}-\d{5}: (?P<message>.*)'
26
+
27
+ change_type_map:
28
+ Break: "Breaking Changes"
29
+ break: "Breaking Changes"
30
+ Feature: "Features"
31
+ feature: "Features"
32
+ Fix: "Bug Fixes"
33
+ fix: "Bug Fixes"
34
+ Bug: "Bug Fixes"
35
+ bug: "Bug Fixes"
36
+
37
+ change_type_order:
38
+ - break
39
+ - Break
40
+ - feature
41
+ - Feature
42
+ - fix
43
+ - Fix
44
+ - bug
45
+ - Bug
46
+
47
+ changelog_pattern: '(feature|fix|bug|break|Feature|Fix|Bug|Break)/[A-Z]{3}-\d{5}'
48
+
49
+ example: '👻 feature/TJP-00001: implement sovereign automation'
50
+ message_template: '{{change_type}}: {{message}}'
51
+
52
+ questions:
53
+ - name: change_type
54
+ type: list
55
+ message: Select the type of change you are committing
56
+ choices:
57
+ - {name: "break: A breaking change", value: break}
58
+ - {name: "feature: A new feature", value: feature}
59
+ - {name: "fix: A standard fix", value: fix}
60
+ - {name: "bug: A specific bug fix", value: bug}
61
+ - name: message
62
+ type: input
63
+ message: "Enter a brief description:"
64
+
65
+ schema: '<type>/<ticket>: <body>'
66
+ # Schema stays strict for your local pre-commit hooks
67
+ schema_pattern: '^.*(feature|fix|bug|break|Feature|Fix|Bug|Break)/[A-Z]{3}-\d{5}: (.*)'
@@ -0,0 +1,5 @@
1
+ # Global owners
2
+ * @thomaspeoples
3
+
4
+ # Specialised owners
5
+ # /src/ @thomaspeoples
@@ -0,0 +1,42 @@
1
+ # 👻 Ghost Stack Pull Request
2
+
3
+ ## 🎯 Purpose
4
+ _What are we haunting today? Briefly describe the change._
5
+
6
+ **Fixes:** # (Link the Gitea Issue here)
7
+
8
+ ---
9
+
10
+ ## 🛠️ Proposed Changes
11
+ - [ ] Logic updated in `src/`
12
+ - [ ] Dependencies synced via `uv lock`
13
+ - [ ] Version bumped in `pyproject.toml` (if manual)
14
+
15
+ ---
16
+
17
+ ## 🚦 Quality Gate (The Ghost Protocol)
18
+ The following **must** be green before the `ghost-bot` is summoned:
19
+
20
+ - [ ] **uv Sync:** Environment is healthy and `uv.lock` is up to date.
21
+ - [ ] **Tests:** `./bin/run_tests.sh` passes with >80% coverage.
22
+ - [ ] **Linting:** `pre-commit run --all-files` passes (No badly formatted code).
23
+ - [ ] **Commits:** Messages follow the `cz_customize` regex (Fix/Feature/Break).
24
+
25
+ ---
26
+
27
+ ## 🧪 Deployment & Verification
28
+ - [ ] Branch is synced with the latest `main`.
29
+ - [ ] Solution has been executed in the local `uv` environment.
30
+ - [ ] (Optional) Verified in the `ci-images` container.
31
+
32
+ ---
33
+
34
+ ## 👤 Author's Final Word
35
+ _Any specific notes for the reviewer or warnings about breaking changes?_
36
+
37
+ ---
38
+
39
+ ## 🕵️ Reviewer Checklist
40
+ - [ ] Code follows the Sovereign style (Clean, no artifacts).
41
+ - [ ] Test coverage is actually meaningful (not just hitting lines).
42
+ - [ ] Versioning strategy aligns with the change type.
@@ -0,0 +1,64 @@
1
+ name: Deploy Gitea Pages
2
+ on:
3
+ workflow_dispatch:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ deploy:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+ with:
14
+ fetch-depth: 0
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v3
18
+ with:
19
+ version: "latest"
20
+
21
+ - name: Set up Python
22
+ run: uv python install 3.12
23
+
24
+ - name: Install dependencies
25
+ run: uv sync --all-extras
26
+
27
+ - name: Run Tests & Coverage
28
+ run: uv run pytest --cov=src --cov-report=xml
29
+
30
+ - name: Generate Coverage Badge
31
+ run: uv run genbadge coverage -i coverage.xml -o docs/coverage.svg
32
+
33
+ - name: Build Documentation
34
+ run: uv run mkdocs build
35
+
36
+ - name: Sync to Main Website Repo
37
+ env:
38
+ MY_TOKEN: ${{ secrets.GITEATOKEN }}
39
+ MY_URL: ${{ secrets.GITEAURL }}
40
+
41
+ run: |
42
+ # 1. Clone your main website repo
43
+ CLEAN_URL=$(echo $MY_URL | sed -e 's|^[^/]*//||')
44
+ git clone https://${MY_TOKEN}@${CLEAN_URL}/thomaspeoples/ghost-site.git main_site
45
+
46
+ # 2. Create the target directory if it doesn't exist
47
+ TARGET_DIR="main_site/www/gitea-repos/wraith-modelgen"
48
+ mkdir -p $TARGET_DIR
49
+
50
+ # 3. Clean and Copy
51
+ rm -rf $TARGET_DIR/*
52
+ cp -r site/* $TARGET_DIR/
53
+
54
+ # 4. Commit and Push to the OTHER repo
55
+ cd main_site
56
+ git config user.name "Ghost Runner"
57
+ git config user.email "runner@ghost-stack.local"
58
+ git add .
59
+ if git diff --staged --quiet; then
60
+ echo "No changes to documentation. Skipping commit."
61
+ else
62
+ git commit -m ":books: main: Update docs for wraith-modelgen: ${{ gitea.sha }}"
63
+ git push origin main
64
+ fi
@@ -0,0 +1,23 @@
1
+ name: 'Sovereign Release'
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ release:
10
+ if: "!contains(github.event.head_commit.message, '[skip ci]')"
11
+ runs-on: ghost-runner
12
+ steps:
13
+ - name: Check out code
14
+ uses: actions/checkout@v4
15
+ with:
16
+ fetch-depth: 0
17
+ token: ${{ secrets.giteatoken }}
18
+
19
+ - name: Run Ghost Bump
20
+ uses: https://git.thomaspeoples.com/thomaspeoples/ci-actions/version-bump@main
21
+ with:
22
+ gitea_token: ${{ secrets.giteatoken }}
23
+ api_url: "${{ secrets.giteaurl }}/api/v1"
@@ -0,0 +1,31 @@
1
+ name: Wraith-Modelgen CI
2
+
3
+ on:
4
+ push:
5
+ branches-ignore:
6
+ - 'main'
7
+
8
+ jobs:
9
+ test-and-verify:
10
+ runs-on: ghost-runner
11
+ steps:
12
+ - name: Check out code
13
+ uses: actions/checkout@v4
14
+ with:
15
+ fetch-depth: 0
16
+ - name: Run Production Tests
17
+ run: |
18
+ uv sync --all-extras --all-groups
19
+ uv run poe test-ci
20
+
21
+ auto-merge:
22
+ needs: test-and-verify
23
+ if: github.ref != 'refs/heads/main'
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+ - name: Call Central Merge Action
28
+ uses: https://git.thomaspeoples.com/thomaspeoples/ci-actions/auto-merge@main
29
+ with:
30
+ gitea_token: ${{ secrets.giteatoken }}
31
+ api_url: "${{ secrets.giteaurl }}/api/v1"
@@ -0,0 +1,167 @@
1
+ # uv and local environment
2
+ .uv/
3
+ ollama_data/
4
+
5
+ # Byte-compiled / optimized / DLL files
6
+ **/__pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+
10
+ # C extensions
11
+ *.so
12
+
13
+ # Distribution / packaging
14
+ .Python
15
+ build/
16
+ develop-eggs/
17
+ dist/
18
+ downloads/
19
+ eggs/
20
+ .eggs/
21
+ lib/
22
+ lib64/
23
+ parts/
24
+ sdist/
25
+ var/
26
+ wheels/
27
+ share/python-wheels/
28
+ *.egg-info/
29
+ .installed.cfg
30
+ *.egg
31
+ MANIFEST
32
+
33
+ # PyInstaller
34
+ # Usually these files are written by a python script from a template
35
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
36
+ *.manifest
37
+ *.spec
38
+
39
+ # Installer logs
40
+ pip-log.txt
41
+ pip-delete-this-directory.txt
42
+
43
+ # Unit test / coverage reports
44
+ htmlcov/
45
+ .tox/
46
+ .nox/
47
+ .coverage
48
+ .coverage.*
49
+ .cache
50
+ nosetests.xml
51
+ coverage.xml
52
+ *.cover
53
+ *.py,cover
54
+ .hypothesis/
55
+ .pytest_cache/
56
+ cover/
57
+
58
+ # Translations
59
+ *.mo
60
+ *.pot
61
+
62
+ # Django stuff:
63
+ *.log
64
+ local_settings.py
65
+ db.sqlite3
66
+ db.sqlite3-journal
67
+
68
+ # Flask stuff:
69
+ instance/
70
+ .webassets-cache
71
+
72
+ # Scrapy stuff:
73
+ .scrapy
74
+
75
+ # Sphinx documentation
76
+ docs/_build/
77
+
78
+ # PyBuilder
79
+ .pybuilder/
80
+ target/
81
+
82
+ # Jupyter Notebook
83
+ .ipynb_checkpoints
84
+
85
+ # IPython
86
+ profile_default/
87
+ ipython_config.py
88
+
89
+ # pyenv
90
+ # For a library or package, you might want to ignore these files since the code is
91
+ # intended to run in multiple environments; otherwise, check them in:
92
+ # .python-version
93
+
94
+ # pipenv
95
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
96
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
97
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
98
+ # install all needed dependencies.
99
+ #Pipfile.lock
100
+
101
+ # poetry
102
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
103
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
104
+ # commonly ignored for libraries.
105
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
106
+ #poetry.lock
107
+
108
+ # pdm
109
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
110
+ #pdm.lock
111
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
112
+ # in version control.
113
+ # https://pdm.fming.dev/latest/usage/project/#working-with-version-control
114
+ .pdm.toml
115
+ .pdm-python
116
+ .pdm-build/
117
+
118
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
119
+ __pypackages__/
120
+
121
+ # Celery stuff
122
+ celerybeat-schedule
123
+ celerybeat.pid
124
+
125
+ # SageMath parsed files
126
+ *.sage.py
127
+
128
+ # Environments
129
+ .env
130
+ .venv
131
+ env/
132
+ venv/
133
+ ENV/
134
+ env.bak/
135
+ venv.bak/
136
+
137
+ # Spyder project settings
138
+ .spyderproject
139
+ .spyproject
140
+
141
+ # Rope project settings
142
+ .ropeproject
143
+
144
+ # mkdocs documentation
145
+ /site
146
+
147
+ # mypy
148
+ .mypy_cache/
149
+ .dmypy.json
150
+ dmypy.json
151
+
152
+ # Pyre type checker
153
+ .pyre/
154
+
155
+ # pytype static type analyzer
156
+ .pytype/
157
+
158
+ # Cython debug symbols
159
+ cython_debug/
160
+
161
+ # PyCharm
162
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
163
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
164
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
165
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
166
+ #.idea/
167
+ .aider*
@@ -0,0 +1,68 @@
1
+ minimum_pre_commit_version: 4.0.0
2
+ default_stages: [pre-commit, pre-push]
3
+
4
+ repos:
5
+ - repo: https://github.com/pre-commit/pre-commit-hooks
6
+ rev: v6.0.0
7
+ hooks:
8
+ - id: trailing-whitespace
9
+ args: [--markdown-linebreak-ext=md]
10
+ - id: end-of-file-fixer
11
+ - id: check-yaml
12
+ - id: check-toml
13
+ - id: check-added-large-files
14
+ - id: check-case-conflict
15
+
16
+ - repo: https://github.com/astral-sh/ruff-pre-commit
17
+ rev: v0.15.9
18
+ hooks:
19
+ - id: ruff
20
+ args: ["--fix", "--exit-non-zero-on-fix"]
21
+ - id: ruff-format
22
+
23
+ - repo: https://github.com/codespell-project/codespell
24
+ rev: v2.4.2
25
+ hooks:
26
+ - id: codespell
27
+ types_or: [python, markdown, rst]
28
+
29
+ - repo: https://github.com/asottile/pyupgrade
30
+ rev: v3.21.2
31
+ hooks:
32
+ - id: pyupgrade
33
+ args: [--py312-plus]
34
+
35
+ - repo: https://github.com/shellcheck-py/shellcheck-py
36
+ rev: v0.11.0.1
37
+ hooks:
38
+ - id: shellcheck
39
+ args: ["--severity=warning"]
40
+
41
+ - repo: https://github.com/Yelp/detect-secrets
42
+ rev: v1.5.0
43
+ hooks:
44
+ - id: detect-secrets
45
+ args: ["--baseline", ".secrets.baseline"]
46
+ exclude: package-lock.json
47
+
48
+ - repo: local
49
+ hooks:
50
+ - id: ty-check
51
+ name: ty check
52
+ entry: uv run ty check
53
+ language: system
54
+ types: [python]
55
+ pass_filenames: false
56
+
57
+ - id: pytest-coverage
58
+ name: pytest-coverage
59
+ entry: uv run poe test-ci
60
+ language: system
61
+ pass_filenames: false
62
+ always_run: true
63
+
64
+ - id: commitizen
65
+ name: commitizen check
66
+ entry: uv run cz check --commit-msg-file
67
+ language: system
68
+ stages: [commit-msg]
@@ -0,0 +1,127 @@
1
+ {
2
+ "version": "1.5.0",
3
+ "plugins_used": [
4
+ {
5
+ "name": "ArtifactoryDetector"
6
+ },
7
+ {
8
+ "name": "AWSKeyDetector"
9
+ },
10
+ {
11
+ "name": "AzureStorageKeyDetector"
12
+ },
13
+ {
14
+ "name": "Base64HighEntropyString",
15
+ "limit": 4.5
16
+ },
17
+ {
18
+ "name": "BasicAuthDetector"
19
+ },
20
+ {
21
+ "name": "CloudantDetector"
22
+ },
23
+ {
24
+ "name": "DiscordBotTokenDetector"
25
+ },
26
+ {
27
+ "name": "GitHubTokenDetector"
28
+ },
29
+ {
30
+ "name": "GitLabTokenDetector"
31
+ },
32
+ {
33
+ "name": "HexHighEntropyString",
34
+ "limit": 3.0
35
+ },
36
+ {
37
+ "name": "IbmCloudIamDetector"
38
+ },
39
+ {
40
+ "name": "IbmCosHmacDetector"
41
+ },
42
+ {
43
+ "name": "IPPublicDetector"
44
+ },
45
+ {
46
+ "name": "JwtTokenDetector"
47
+ },
48
+ {
49
+ "name": "KeywordDetector",
50
+ "keyword_exclude": ""
51
+ },
52
+ {
53
+ "name": "MailchimpDetector"
54
+ },
55
+ {
56
+ "name": "NpmDetector"
57
+ },
58
+ {
59
+ "name": "OpenAIDetector"
60
+ },
61
+ {
62
+ "name": "PrivateKeyDetector"
63
+ },
64
+ {
65
+ "name": "PypiTokenDetector"
66
+ },
67
+ {
68
+ "name": "SendGridDetector"
69
+ },
70
+ {
71
+ "name": "SlackDetector"
72
+ },
73
+ {
74
+ "name": "SoftlayerDetector"
75
+ },
76
+ {
77
+ "name": "SquareOAuthDetector"
78
+ },
79
+ {
80
+ "name": "StripeDetector"
81
+ },
82
+ {
83
+ "name": "TelegramBotTokenDetector"
84
+ },
85
+ {
86
+ "name": "TwilioKeyDetector"
87
+ }
88
+ ],
89
+ "filters_used": [
90
+ {
91
+ "path": "detect_secrets.filters.allowlist.is_line_allowlisted"
92
+ },
93
+ {
94
+ "path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
95
+ "min_level": 2
96
+ },
97
+ {
98
+ "path": "detect_secrets.filters.heuristic.is_indirect_reference"
99
+ },
100
+ {
101
+ "path": "detect_secrets.filters.heuristic.is_likely_id_string"
102
+ },
103
+ {
104
+ "path": "detect_secrets.filters.heuristic.is_lock_file"
105
+ },
106
+ {
107
+ "path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
108
+ },
109
+ {
110
+ "path": "detect_secrets.filters.heuristic.is_potential_uuid"
111
+ },
112
+ {
113
+ "path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
114
+ },
115
+ {
116
+ "path": "detect_secrets.filters.heuristic.is_sequential_string"
117
+ },
118
+ {
119
+ "path": "detect_secrets.filters.heuristic.is_swagger_file"
120
+ },
121
+ {
122
+ "path": "detect_secrets.filters.heuristic.is_templated_secret"
123
+ }
124
+ ],
125
+ "results": {},
126
+ "generated_at": "2026-05-16T04:52:58Z"
127
+ }
@@ -0,0 +1,41 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0
4
+
5
+ Architectural rebuild. Not backwards compatible with 0.2.x contracts.
6
+
7
+ ### Changed
8
+
9
+ - **BigQuery-only.** Dropped SQLAlchemy in favour of `google-cloud-bigquery`. Removed warehouse-specific extras.
10
+ - **Introspection is now part of `gen`.** Removed the standalone `introspect` command. Contracts are now minimal; column metadata comes from the warehouse at generation time.
11
+ - **Layer responsibility swap.** Raw is now a `select *` pass-through. Casts and renames live in staging where they belong.
12
+ - **One layer per invocation.** `--raw` and `--staging` are mutually exclusive flags; the `--layer all` option is gone.
13
+ - **Contract format.** `unique_key` and `loaded_at_field` now name source-side columns. Staging columns declare both `source:` and `name:` explicitly.
14
+
15
+ ### Added
16
+
17
+ - `partition_by` and `cluster_by` config per layer (BQ-specific).
18
+ - Composite `unique_key` support (single string or list).
19
+ - Schema drift validation: contract references missing from source fail loudly with column names.
20
+ - Schema evolution notification: new source columns reported in CLI output.
21
+ - `IntrospectedColumn.dbt_data_type` handles REPEATED mode (wraps in `ARRAY<>`) and RECORD type (renders as STRUCT).
22
+ - Contract version validation.
23
+ - Determinism guarantee: same contract + same source = byte-identical output.
24
+ - `FakeIntrospector` for tests and offline development.
25
+ - Structured error types: `ContractError`, `GenerationError`, `IntrospectionError`.
26
+
27
+ ## 0.2.0
28
+
29
+ Three-layer generation (landing -> raw -> staging) with SQLAlchemy-based introspection. Superseded.
30
+
31
+ ## 0.1.0
32
+
33
+ Initial release. Single-layer staging model from YAML contract.
34
+
35
+ ## v0.4.0 (2026-05-16)
36
+
37
+ ## v0.3.0 (2026-05-16)
38
+
39
+ ## v0.2.0 (2026-05-16)
40
+
41
+ ## v0.1.0 (2026-05-16)