mtg-carddb 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 (73) hide show
  1. mtg_carddb-0.1.0/.github/workflows/ci.yml +94 -0
  2. mtg_carddb-0.1.0/.github/workflows/publish.yml +112 -0
  3. mtg_carddb-0.1.0/.gitignore +220 -0
  4. mtg_carddb-0.1.0/CHANGELOG.md +33 -0
  5. mtg_carddb-0.1.0/CONTRIBUTING.md +72 -0
  6. mtg_carddb-0.1.0/LICENSE +21 -0
  7. mtg_carddb-0.1.0/PKG-INFO +408 -0
  8. mtg_carddb-0.1.0/README.md +392 -0
  9. mtg_carddb-0.1.0/docs/api.md +469 -0
  10. mtg_carddb-0.1.0/docs/releasing.md +195 -0
  11. mtg_carddb-0.1.0/docs/schema.md +365 -0
  12. mtg_carddb-0.1.0/pyproject.toml +35 -0
  13. mtg_carddb-0.1.0/scripts/audit_drilldown.py +214 -0
  14. mtg_carddb-0.1.0/scripts/audit_real_data.py +504 -0
  15. mtg_carddb-0.1.0/scripts/audit_remaining_types.py +153 -0
  16. mtg_carddb-0.1.0/scripts/audit_staging.py +412 -0
  17. mtg_carddb-0.1.0/scripts/export_failed_printings.py +50 -0
  18. mtg_carddb-0.1.0/src/mtg_carddb/__init__.py +84 -0
  19. mtg_carddb-0.1.0/src/mtg_carddb/__main__.py +7 -0
  20. mtg_carddb-0.1.0/src/mtg_carddb/acquisition.py +330 -0
  21. mtg_carddb-0.1.0/src/mtg_carddb/activation.py +363 -0
  22. mtg_carddb-0.1.0/src/mtg_carddb/builder.py +275 -0
  23. mtg_carddb-0.1.0/src/mtg_carddb/bulk.py +115 -0
  24. mtg_carddb-0.1.0/src/mtg_carddb/catalogs.py +515 -0
  25. mtg_carddb-0.1.0/src/mtg_carddb/cli.py +426 -0
  26. mtg_carddb-0.1.0/src/mtg_carddb/database.py +636 -0
  27. mtg_carddb-0.1.0/src/mtg_carddb/freshness.py +199 -0
  28. mtg_carddb-0.1.0/src/mtg_carddb/import_policy.py +100 -0
  29. mtg_carddb-0.1.0/src/mtg_carddb/indexes.py +138 -0
  30. mtg_carddb-0.1.0/src/mtg_carddb/ingestion.py +221 -0
  31. mtg_carddb-0.1.0/src/mtg_carddb/jsonl_limits.py +73 -0
  32. mtg_carddb-0.1.0/src/mtg_carddb/lockfiles.py +229 -0
  33. mtg_carddb-0.1.0/src/mtg_carddb/models.py +373 -0
  34. mtg_carddb-0.1.0/src/mtg_carddb/network.py +115 -0
  35. mtg_carddb-0.1.0/src/mtg_carddb/normalization.py +702 -0
  36. mtg_carddb-0.1.0/src/mtg_carddb/oracle_tags.py +362 -0
  37. mtg_carddb-0.1.0/src/mtg_carddb/persistence.py +90 -0
  38. mtg_carddb-0.1.0/src/mtg_carddb/printed_types.py +182 -0
  39. mtg_carddb-0.1.0/src/mtg_carddb/provenance.py +192 -0
  40. mtg_carddb-0.1.0/src/mtg_carddb/reference_integration.py +84 -0
  41. mtg_carddb-0.1.0/src/mtg_carddb/reference_sources.py +575 -0
  42. mtg_carddb-0.1.0/src/mtg_carddb/representatives.py +273 -0
  43. mtg_carddb-0.1.0/src/mtg_carddb/schema.py +232 -0
  44. mtg_carddb-0.1.0/src/mtg_carddb/storage.py +373 -0
  45. mtg_carddb-0.1.0/src/mtg_carddb/workflow.py +294 -0
  46. mtg_carddb-0.1.0/tests/test_acquisition.py +523 -0
  47. mtg_carddb-0.1.0/tests/test_activation.py +522 -0
  48. mtg_carddb-0.1.0/tests/test_audit_staging.py +164 -0
  49. mtg_carddb-0.1.0/tests/test_builder.py +371 -0
  50. mtg_carddb-0.1.0/tests/test_bulk.py +177 -0
  51. mtg_carddb-0.1.0/tests/test_catalogs.py +508 -0
  52. mtg_carddb-0.1.0/tests/test_cli.py +544 -0
  53. mtg_carddb-0.1.0/tests/test_database.py +452 -0
  54. mtg_carddb-0.1.0/tests/test_freshness.py +262 -0
  55. mtg_carddb-0.1.0/tests/test_import_policy.py +79 -0
  56. mtg_carddb-0.1.0/tests/test_indexes.py +184 -0
  57. mtg_carddb-0.1.0/tests/test_ingestion.py +332 -0
  58. mtg_carddb-0.1.0/tests/test_jsonl_limits.py +104 -0
  59. mtg_carddb-0.1.0/tests/test_lockfiles.py +179 -0
  60. mtg_carddb-0.1.0/tests/test_models.py +301 -0
  61. mtg_carddb-0.1.0/tests/test_network.py +211 -0
  62. mtg_carddb-0.1.0/tests/test_normalization.py +583 -0
  63. mtg_carddb-0.1.0/tests/test_oracle_tags.py +278 -0
  64. mtg_carddb-0.1.0/tests/test_persistence.py +55 -0
  65. mtg_carddb-0.1.0/tests/test_printed_types.py +131 -0
  66. mtg_carddb-0.1.0/tests/test_provenance.py +166 -0
  67. mtg_carddb-0.1.0/tests/test_public_api.py +148 -0
  68. mtg_carddb-0.1.0/tests/test_reference_integration.py +189 -0
  69. mtg_carddb-0.1.0/tests/test_reference_sources.py +390 -0
  70. mtg_carddb-0.1.0/tests/test_representatives.py +518 -0
  71. mtg_carddb-0.1.0/tests/test_schema.py +113 -0
  72. mtg_carddb-0.1.0/tests/test_storage.py +227 -0
  73. mtg_carddb-0.1.0/tests/test_workflow.py +573 -0
@@ -0,0 +1,94 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ tests:
13
+ name: Tests (${{ matrix.os }}, Python ${{ matrix.python }})
14
+ runs-on: ${{ matrix.os }}
15
+ timeout-minutes: 20
16
+
17
+ strategy:
18
+ fail-fast: false
19
+ matrix:
20
+ os: [ubuntu-latest, windows-latest, macos-latest]
21
+ python: ["3.11", "3.12", "3.13"]
22
+
23
+ steps:
24
+ - uses: actions/checkout@v7
25
+ with:
26
+ persist-credentials: false
27
+
28
+ - uses: actions/setup-python@v7
29
+ with:
30
+ python-version: ${{ matrix.python }}
31
+
32
+ - name: Install package and development tools
33
+ run: python -m pip install -e ".[dev]"
34
+
35
+ - name: Run tests with network access blocked
36
+ run: python -m pytest -q --disable-socket
37
+
38
+ distributions:
39
+ name: Build and test distributions
40
+ runs-on: ubuntu-latest
41
+ timeout-minutes: 20
42
+
43
+ steps:
44
+ - uses: actions/checkout@v7
45
+ with:
46
+ persist-credentials: false
47
+
48
+ - uses: actions/setup-python@v7
49
+ with:
50
+ python-version: "3.11"
51
+
52
+ - name: Install build tools
53
+ run: python -m pip install "build>=1.2" "twine>=6"
54
+
55
+ - name: Build wheel and source distribution
56
+ run: python -m build
57
+
58
+ - name: Validate distribution metadata
59
+ run: python -m twine check --strict dist/*
60
+
61
+ - name: Smoke-test wheel in isolated environment
62
+ shell: bash
63
+ run: |
64
+ python -m venv "$RUNNER_TEMP/carddb-wheel"
65
+ "$RUNNER_TEMP/carddb-wheel/bin/python" -m pip install --no-index --no-deps dist/*.whl
66
+
67
+ cd "$RUNNER_TEMP"
68
+
69
+ "$RUNNER_TEMP/carddb-wheel/bin/python" -I -c 'from pathlib import Path; import mtg_carddb as m; assert "site-packages" in str(Path(m.__file__).resolve()); assert callable(m.initialize_database); print(m.__file__)'
70
+
71
+ "$RUNNER_TEMP/carddb-wheel/bin/python" -m mtg_carddb --help
72
+ "$RUNNER_TEMP/carddb-wheel/bin/mtg-carddb" --help
73
+ "$RUNNER_TEMP/carddb-wheel/bin/mtg-carddb" locks --database "$RUNNER_TEMP/nonexistent-carddb.sqlite"
74
+
75
+ - name: Smoke-test sdist in isolated environment
76
+ shell: bash
77
+ run: |
78
+ python -m venv "$RUNNER_TEMP/carddb-sdist"
79
+ "$RUNNER_TEMP/carddb-sdist/bin/python" -m pip install --no-deps dist/*.tar.gz
80
+
81
+ cd "$RUNNER_TEMP"
82
+
83
+ "$RUNNER_TEMP/carddb-sdist/bin/python" -I -c 'from pathlib import Path; import mtg_carddb as m; assert "site-packages" in str(Path(m.__file__).resolve()); assert callable(m.update_database); print(m.__file__)'
84
+
85
+ "$RUNNER_TEMP/carddb-sdist/bin/python" -m mtg_carddb --help
86
+ "$RUNNER_TEMP/carddb-sdist/bin/mtg-carddb" --help
87
+
88
+ - name: Preserve validated distributions
89
+ uses: actions/upload-artifact@v7
90
+ with:
91
+ name: mtg-carddb-distributions
92
+ path: dist/*
93
+ if-no-files-found: error
94
+ retention-days: 7
@@ -0,0 +1,112 @@
1
+ # Publishing runs only when the maintainer manually starts this workflow.
2
+ name: Publish to PyPI
3
+
4
+ on:
5
+ workflow_dispatch:
6
+ inputs:
7
+ tag:
8
+ description: "Existing release tag to publish (for example, v0.1.0)"
9
+ required: true
10
+ type: string
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ concurrency:
16
+ group: pypi-${{ inputs.tag }}
17
+ cancel-in-progress: false
18
+
19
+ jobs:
20
+ build:
21
+ name: Verify and build tagged release
22
+ runs-on: ubuntu-latest
23
+ timeout-minutes: 20
24
+ steps:
25
+ - name: Validate tag input
26
+ shell: bash
27
+ env:
28
+ RELEASE_TAG: ${{ inputs.tag }}
29
+ run: |
30
+ set -euo pipefail
31
+ if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
32
+ echo "Expected a version tag like v0.1.0" >&2
33
+ exit 1
34
+ fi
35
+
36
+ - name: Check out the requested tag
37
+ uses: actions/checkout@v7
38
+ with:
39
+ ref: refs/tags/${{ inputs.tag }}
40
+ persist-credentials: false
41
+
42
+ - name: Set up Python
43
+ uses: actions/setup-python@v7
44
+ with:
45
+ python-version: "3.11"
46
+
47
+ - name: Confirm tag matches package version
48
+ env:
49
+ RELEASE_TAG: ${{ inputs.tag }}
50
+ run: |
51
+ python - <<'PY'
52
+ import os
53
+ from pathlib import Path
54
+ import tomllib
55
+
56
+ project = tomllib.loads(Path("pyproject.toml").read_text(encoding="utf-8"))["project"]
57
+ actual = os.environ["RELEASE_TAG"]
58
+ expected = f"v{project['version']}"
59
+ if actual != expected:
60
+ raise SystemExit(f"Tag {actual!r} does not match package version {expected!r}")
61
+ if project["name"] != "mtg-carddb":
62
+ raise SystemExit("Unexpected distribution name")
63
+ PY
64
+
65
+ - name: Install development dependencies
66
+ run: python -m pip install -e ".[dev]"
67
+
68
+ - name: Run offline tests
69
+ run: python -m pytest -q --disable-socket
70
+
71
+ - name: Build wheel and source distribution
72
+ run: python -m build
73
+
74
+ - name: Check distribution metadata and count
75
+ shell: bash
76
+ run: |
77
+ python -m twine check --strict dist/*
78
+ python - <<'PY'
79
+ from pathlib import Path
80
+ files = list(Path("dist").iterdir())
81
+ wheels = [p for p in files if p.suffix == ".whl"]
82
+ sdists = [p for p in files if p.name.endswith(".tar.gz")]
83
+ if len(files) != 2 or len(wheels) != 1 or len(sdists) != 1:
84
+ raise SystemExit(f"Expected one wheel and one sdist; found: {[p.name for p in files]}")
85
+ PY
86
+
87
+ - name: Save validated distributions
88
+ uses: actions/upload-artifact@v7
89
+ with:
90
+ name: mtg-carddb-release-dists
91
+ path: dist/
92
+ if-no-files-found: error
93
+ retention-days: 7
94
+
95
+ publish:
96
+ name: Publish validated distributions
97
+ needs: build
98
+ runs-on: ubuntu-latest
99
+ environment:
100
+ name: pypi
101
+ url: https://pypi.org/project/mtg-carddb/
102
+ permissions:
103
+ id-token: write
104
+ steps:
105
+ - name: Download validated distributions
106
+ uses: actions/download-artifact@v7
107
+ with:
108
+ name: mtg-carddb-release-dists
109
+ path: dist/
110
+
111
+ - name: Publish to PyPI using Trusted Publishing
112
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,220 @@
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
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ *.lcov
51
+ .hypothesis/
52
+ .pytest_cache/
53
+ cover/
54
+
55
+ # Translations
56
+ *.mo
57
+ *.pot
58
+
59
+ # Django stuff:
60
+ *.log
61
+ local_settings.py
62
+ db.sqlite3
63
+ db.sqlite3-journal
64
+
65
+ # Flask stuff:
66
+ instance/
67
+ .webassets-cache
68
+
69
+ # Scrapy stuff:
70
+ .scrapy
71
+
72
+ # Sphinx documentation
73
+ docs/_build/
74
+
75
+ # PyBuilder
76
+ .pybuilder/
77
+ target/
78
+
79
+ # Jupyter Notebook
80
+ .ipynb_checkpoints
81
+
82
+ # IPython
83
+ profile_default/
84
+ ipython_config.py
85
+
86
+ # pyenv
87
+ # For a library or package, you might want to ignore these files since the code is
88
+ # intended to run in multiple environments; otherwise, check them in:
89
+ # .python-version
90
+
91
+ # pipenv
92
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
93
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
94
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
95
+ # install all needed dependencies.
96
+ # Pipfile.lock
97
+
98
+ # uv
99
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
100
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
101
+ # commonly ignored for libraries.
102
+ # uv.lock
103
+
104
+ # poetry
105
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
106
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
107
+ # commonly ignored for libraries.
108
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
109
+ # poetry.lock
110
+ # poetry.toml
111
+
112
+ # pdm
113
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
114
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
115
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
116
+ # pdm.lock
117
+ # pdm.toml
118
+ .pdm-python
119
+ .pdm-build/
120
+
121
+ # pixi
122
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
123
+ # pixi.lock
124
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
125
+ # in the .venv directory. It is recommended not to include this directory in version control.
126
+ .pixi/*
127
+ !.pixi/config.toml
128
+
129
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
130
+ __pypackages__/
131
+
132
+ # Celery stuff
133
+ celerybeat-schedule*
134
+ celerybeat.pid
135
+
136
+ # Redis
137
+ *.rdb
138
+ *.aof
139
+ *.pid
140
+
141
+ # RabbitMQ
142
+ mnesia/
143
+ rabbitmq/
144
+ rabbitmq-data/
145
+
146
+ # ActiveMQ
147
+ activemq-data/
148
+
149
+ # SageMath parsed files
150
+ *.sage.py
151
+
152
+ # Environments
153
+ .env
154
+ .envrc
155
+ .venv
156
+ env/
157
+ venv/
158
+ ENV/
159
+ env.bak/
160
+ venv.bak/
161
+
162
+ # Spyder project settings
163
+ .spyderproject
164
+ .spyproject
165
+
166
+ # Rope project settings
167
+ .ropeproject
168
+
169
+ # mkdocs/Zensical documentation
170
+ /site
171
+
172
+ # mypy
173
+ .mypy_cache/
174
+ .dmypy.json
175
+ dmypy.json
176
+
177
+ # Pyre type checker
178
+ .pyre/
179
+
180
+ # pytype static type analyzer
181
+ .pytype/
182
+
183
+ # Cython debug symbols
184
+ cython_debug/
185
+
186
+ # PyCharm
187
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
188
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
189
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
190
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
191
+ # .idea/
192
+
193
+ # Abstra
194
+ # Abstra is an AI-powered process automation framework.
195
+ # Ignore directories containing user credentials, local state, and settings.
196
+ # Learn more at https://abstra.io/docs
197
+ .abstra/
198
+
199
+ # Visual Studio Code
200
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore that
201
+ # can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
202
+ # and can be added to the global gitignore or merged into this file. However, if you prefer, you
203
+ # could uncomment the following to ignore the entire vscode folder
204
+ # .vscode/
205
+ # Temporary file for partial code execution
206
+ tempCodeRunnerFile.py
207
+
208
+ # Ruff stuff:
209
+ .ruff_cache/
210
+
211
+ # PyPI configuration file
212
+ .pypirc
213
+
214
+ # Marimo
215
+ marimo/_static/
216
+ marimo/_lsp/
217
+ __marimo__/
218
+
219
+ # Streamlit
220
+ .streamlit/secrets.toml
@@ -0,0 +1,33 @@
1
+ # Changelog
2
+
3
+ Notable user-facing changes to MTG CardDB are recorded here.
4
+
5
+ The project follows a `MAJOR.MINOR.PATCH` version scheme. Releases below 1.0.0 may refine the public API, while SQLite schema compatibility is tracked separately using `PRAGMA user_version`.
6
+
7
+ ## Unreleased
8
+
9
+ No changes recorded yet.
10
+
11
+ ## 0.1.0 — Initial release candidate
12
+
13
+ ### Added
14
+
15
+ - Immutable, composable Oracle-ID search API.
16
+ - Read-only SQLite-backed card records and associated metadata.
17
+ - Color identity, color, type, keyword, mana value, produced-mana, legality, Oracle Tag, and historical set/rarity/game queries.
18
+ - Normalization of multiface and reversible-card records.
19
+ - Deterministic Oracle representative and legality selection.
20
+ - Scryfall Default Cards, Oracle Tags, Sets API, and type-catalog acquisition.
21
+ - Verified provenance and source-freshness reporting.
22
+ - Explicit database initialization, update, information, and lock-inspection CLI commands.
23
+ - Progress reporting during database builds.
24
+ - Validated snapshot activation, guarded updates, and lock diagnostics.
25
+ - Network, download-size, and decompression safeguards.
26
+ - Cross-platform CI and clean distribution-installation checks.
27
+
28
+ ### Known limitations
29
+
30
+ - No individual printing inventory, price history, or historical Oracle text revisions.
31
+ - No automatically retained source archive for exact offline reconstruction.
32
+ - No guaranteed atomic compare-and-replace against unrelated external filesystem writers.
33
+ - Public Python API remains pre-1.0.
@@ -0,0 +1,72 @@
1
+ # Contributing to MTG CardDB
2
+
3
+ Contributions, bug reports, regression tests, and documentation corrections are welcome.
4
+
5
+ MTG CardDB is a local-first Python library. Its central guarantees are immutable Oracle-ID query results, deterministic normalization, read-only database access, and explicit, safe database updates.
6
+
7
+ ## Development setup
8
+
9
+ Python 3.11 or newer is required.
10
+
11
+ Clone the repository, create a virtual environment if desired, and install the development dependencies:
12
+
13
+ ```powershell
14
+ python -m pip install -e ".[dev]"
15
+ ```
16
+
17
+ Run the offline test suite:
18
+
19
+ ```powershell
20
+ python -m pytest -q --disable-socket
21
+ ```
22
+
23
+ The ordinary test suite must not depend on the availability of Scryfall or any other external service. Use temporary files, synthetic fixtures, and mocked HTTP responses when testing network or import behavior.
24
+
25
+ ## Before submitting changes
26
+
27
+ - Add regression tests for corrected bugs and new public behavior.
28
+ - Run the complete offline test suite.
29
+ - Keep imports and database queries free of implicit network requests.
30
+ - Check Windows, Linux, and macOS considerations, especially filesystem replacement, locks, path handling, and timestamps.
31
+ - Update documentation when public behavior changes.
32
+ - Do not commit generated SQLite databases, downloaded Scryfall exports, local credentials, virtual environments, or build artifacts.
33
+
34
+ GitHub Actions tests the supported Python versions on all three operating systems and verifies wheel and source-distribution builds.
35
+
36
+ ## Public API and schema
37
+
38
+ The public API is documented in [docs/api.md](docs/api.md). Prefer exposing stable library interfaces through `mtg_carddb.__init__`.
39
+
40
+ Queries should continue to return native `frozenset[str]` Oracle-ID memberships. Do not substitute printing IDs, mutable result collections, or custom query objects without an explicit API review.
41
+
42
+ The persisted schema is documented in [docs/schema.md](docs/schema.md).
43
+
44
+ **The SQL block in `docs/schema.md` must match `SCHEMA_SQL` in `src/mtg_carddb/schema.py`.** This is enforced by `tests/test_schema.py`.
45
+
46
+ Do not change the SQL schema silently. A schema change requires coordinated changes to its documentation, builder, reader compatibility checks, tests, and SQLite schema version policy.
47
+
48
+ The current implementation is pre-1.0. API changes should still be deliberate and documented, even though its interface is not yet declared fully stable.
49
+
50
+ ## Scryfall import changes
51
+
52
+ The standard workflow consumes Default Cards, Oracle Tags, the Sets API, and type catalogs.
53
+
54
+ Preserve the distinction between known-empty and missing source fields. Keep historical set, rarity, and game observations coupled. Preserve deterministic Oracle representative selection and the separate legality-selection policy.
55
+
56
+ An unsupported required source shape should produce a diagnostic failure, not fabricated values or a partially activated database.
57
+
58
+ Real Scryfall integration runs are separate from the offline test suite. Perform them explicitly when changing normalization, import logic, source acquisition, or the on-disk schema. Avoid unnecessary API requests; use bulk exports rather than per-card downloads.
59
+
60
+ ## Reporting issues
61
+
62
+ When reporting a bug, include the CardDB version, Python version, operating system, the operation attempted, and a minimal reproducible example or traceback.
63
+
64
+ For database build issues, source timestamps, source types, relevant hashes, and sanitized error messages are useful. Do not upload personal files or unrelated database contents without checking what they contain.
65
+
66
+ For security-sensitive reports, avoid posting exploit details publicly before there is an opportunity to investigate.
67
+
68
+ ## Releases
69
+
70
+ See [docs/releasing.md](docs/releasing.md) for the release checklist and compatibility expectations.
71
+
72
+ The project is licensed under the [MIT License](LICENSE). Card data supplied by Scryfall is subject to its own terms and attribution requirements.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jerrett Martin
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.