xtalate 0.1.0.dev0__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 (122) hide show
  1. xtalate-0.1.0.dev0/.claude/settings.local.json +39 -0
  2. xtalate-0.1.0.dev0/.github/workflows/ci.yml +48 -0
  3. xtalate-0.1.0.dev0/.gitignore +230 -0
  4. xtalate-0.1.0.dev0/.import_linter_cache/.gitignore +2 -0
  5. xtalate-0.1.0.dev0/.import_linter_cache/711309a5eafe379c50451bc7ebdb0a6d9c6d28e6.data.json +1 -0
  6. xtalate-0.1.0.dev0/.import_linter_cache/CACHEDIR.TAG +3 -0
  7. xtalate-0.1.0.dev0/.import_linter_cache/xtalate.meta.json +1 -0
  8. xtalate-0.1.0.dev0/CHANGELOG.md +47 -0
  9. xtalate-0.1.0.dev0/CITATION.cff +23 -0
  10. xtalate-0.1.0.dev0/CLAUDE.md +145 -0
  11. xtalate-0.1.0.dev0/LICENSE +201 -0
  12. xtalate-0.1.0.dev0/NOTICE +11 -0
  13. xtalate-0.1.0.dev0/PKG-INFO +161 -0
  14. xtalate-0.1.0.dev0/README.md +134 -0
  15. xtalate-0.1.0.dev0/docs/ARCHITECTURE_REVIEW.md +166 -0
  16. xtalate-0.1.0.dev0/docs/DECISIONS.md +292 -0
  17. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.1.md +201 -0
  18. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.2.md +161 -0
  19. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.3.md +150 -0
  20. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.4.md +128 -0
  21. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.5.md +151 -0
  22. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.6.md +137 -0
  23. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v0.7.md +127 -0
  24. xtalate-0.1.0.dev0/docs/IMPLEMENTATION_PLAN_v1.0.md +127 -0
  25. xtalate-0.1.0.dev0/docs/Incremental_Roadmap_v1.0.md +214 -0
  26. xtalate-0.1.0.dev0/docs/MASTER_SPEC.md +3369 -0
  27. xtalate-0.1.0.dev0/examples/.gitkeep +0 -0
  28. xtalate-0.1.0.dev0/examples/convert_extxyz_to_poscar.py +92 -0
  29. xtalate-0.1.0.dev0/pyproject.toml +110 -0
  30. xtalate-0.1.0.dev0/src/xtalate/__init__.py +10 -0
  31. xtalate-0.1.0.dev0/src/xtalate/capabilities/__init__.py +19 -0
  32. xtalate-0.1.0.dev0/src/xtalate/capabilities/registry.py +126 -0
  33. xtalate-0.1.0.dev0/src/xtalate/cli/__init__.py +11 -0
  34. xtalate-0.1.0.dev0/src/xtalate/cli/main.py +431 -0
  35. xtalate-0.1.0.dev0/src/xtalate/cli/render.py +126 -0
  36. xtalate-0.1.0.dev0/src/xtalate/conversion/__init__.py +48 -0
  37. xtalate-0.1.0.dev0/src/xtalate/conversion/engine.py +560 -0
  38. xtalate-0.1.0.dev0/src/xtalate/conversion/preflight.py +127 -0
  39. xtalate-0.1.0.dev0/src/xtalate/conversion/report.py +74 -0
  40. xtalate-0.1.0.dev0/src/xtalate/discovery/__init__.py +28 -0
  41. xtalate-0.1.0.dev0/src/xtalate/discovery/engine.py +202 -0
  42. xtalate-0.1.0.dev0/src/xtalate/discovery/report.py +50 -0
  43. xtalate-0.1.0.dev0/src/xtalate/discovery/sniffer.py +95 -0
  44. xtalate-0.1.0.dev0/src/xtalate/exporters/__init__.py +34 -0
  45. xtalate-0.1.0.dev0/src/xtalate/exporters/extxyz.py +140 -0
  46. xtalate-0.1.0.dev0/src/xtalate/exporters/poscar.py +180 -0
  47. xtalate-0.1.0.dev0/src/xtalate/exporters/xyz.py +67 -0
  48. xtalate-0.1.0.dev0/src/xtalate/parsers/__init__.py +31 -0
  49. xtalate-0.1.0.dev0/src/xtalate/parsers/_common.py +57 -0
  50. xtalate-0.1.0.dev0/src/xtalate/parsers/extxyz.py +452 -0
  51. xtalate-0.1.0.dev0/src/xtalate/parsers/poscar.py +428 -0
  52. xtalate-0.1.0.dev0/src/xtalate/parsers/xyz.py +286 -0
  53. xtalate-0.1.0.dev0/src/xtalate/py.typed +0 -0
  54. xtalate-0.1.0.dev0/src/xtalate/recovery/__init__.py +41 -0
  55. xtalate-0.1.0.dev0/src/xtalate/recovery/engine.py +321 -0
  56. xtalate-0.1.0.dev0/src/xtalate/recovery/scenarios.py +86 -0
  57. xtalate-0.1.0.dev0/src/xtalate/registry.py +28 -0
  58. xtalate-0.1.0.dev0/src/xtalate/schema/__init__.py +40 -0
  59. xtalate-0.1.0.dev0/src/xtalate/schema/arrays.py +115 -0
  60. xtalate-0.1.0.dev0/src/xtalate/schema/elements.py +150 -0
  61. xtalate-0.1.0.dev0/src/xtalate/schema/models.py +283 -0
  62. xtalate-0.1.0.dev0/src/xtalate/schema/paths.py +88 -0
  63. xtalate-0.1.0.dev0/src/xtalate/schema/presence.py +152 -0
  64. xtalate-0.1.0.dev0/src/xtalate/sdk/__init__.py +27 -0
  65. xtalate-0.1.0.dev0/src/xtalate/sdk/capabilities.py +57 -0
  66. xtalate-0.1.0.dev0/src/xtalate/sdk/plugins.py +72 -0
  67. xtalate-0.1.0.dev0/src/xtalate/sdk/results.py +45 -0
  68. xtalate-0.1.0.dev0/src/xtalate/validation/__init__.py +25 -0
  69. xtalate-0.1.0.dev0/src/xtalate/validation/engine.py +585 -0
  70. xtalate-0.1.0.dev0/src/xtalate/validation/report.py +48 -0
  71. xtalate-0.1.0.dev0/src/xtalate/validation/rethreshold.py +122 -0
  72. xtalate-0.1.0.dev0/src/xtalate/validation/tolerance.py +125 -0
  73. xtalate-0.1.0.dev0/tests/__init__.py +0 -0
  74. xtalate-0.1.0.dev0/tests/_dummy_plugins.py +111 -0
  75. xtalate-0.1.0.dev0/tests/_format_helpers.py +54 -0
  76. xtalate-0.1.0.dev0/tests/capabilities/__init__.py +0 -0
  77. xtalate-0.1.0.dev0/tests/capabilities/test_registry.py +95 -0
  78. xtalate-0.1.0.dev0/tests/cli/__init__.py +0 -0
  79. xtalate-0.1.0.dev0/tests/cli/test_cli.py +220 -0
  80. xtalate-0.1.0.dev0/tests/conversion/__init__.py +0 -0
  81. xtalate-0.1.0.dev0/tests/conversion/test_engine.py +193 -0
  82. xtalate-0.1.0.dev0/tests/conversion/test_preflight.py +111 -0
  83. xtalate-0.1.0.dev0/tests/discovery/__init__.py +0 -0
  84. xtalate-0.1.0.dev0/tests/discovery/test_engine.py +136 -0
  85. xtalate-0.1.0.dev0/tests/discovery/test_sniffer.py +115 -0
  86. xtalate-0.1.0.dev0/tests/exporters/__init__.py +1 -0
  87. xtalate-0.1.0.dev0/tests/golden/.gitkeep +0 -0
  88. xtalate-0.1.0.dev0/tests/golden/extxyz/co-in-cell/expected.canonical.json +119 -0
  89. xtalate-0.1.0.dev0/tests/golden/extxyz/co-in-cell/manifest.yaml +21 -0
  90. xtalate-0.1.0.dev0/tests/golden/extxyz/co-in-cell/sample.extxyz +4 -0
  91. xtalate-0.1.0.dev0/tests/golden/poscar/nacl-primitive/POSCAR +10 -0
  92. xtalate-0.1.0.dev0/tests/golden/poscar/nacl-primitive/expected.canonical.json +115 -0
  93. xtalate-0.1.0.dev0/tests/golden/poscar/nacl-primitive/manifest.yaml +15 -0
  94. xtalate-0.1.0.dev0/tests/golden/schema/poscar_nacl.json +115 -0
  95. xtalate-0.1.0.dev0/tests/golden/schema/xyz_2frame_3atom.json +137 -0
  96. xtalate-0.1.0.dev0/tests/golden/xyz/water-traj/expected.canonical.json +137 -0
  97. xtalate-0.1.0.dev0/tests/golden/xyz/water-traj/manifest.yaml +14 -0
  98. xtalate-0.1.0.dev0/tests/golden/xyz/water-traj/water_traj.xyz +10 -0
  99. xtalate-0.1.0.dev0/tests/parsers/__init__.py +1 -0
  100. xtalate-0.1.0.dev0/tests/parsers/test_builtins_registration.py +82 -0
  101. xtalate-0.1.0.dev0/tests/parsers/test_extxyz.py +205 -0
  102. xtalate-0.1.0.dev0/tests/parsers/test_poscar.py +176 -0
  103. xtalate-0.1.0.dev0/tests/parsers/test_xyz.py +110 -0
  104. xtalate-0.1.0.dev0/tests/recovery/__init__.py +0 -0
  105. xtalate-0.1.0.dev0/tests/recovery/test_engine.py +175 -0
  106. xtalate-0.1.0.dev0/tests/recovery/test_scenarios.py +35 -0
  107. xtalate-0.1.0.dev0/tests/roundtrip/.gitkeep +0 -0
  108. xtalate-0.1.0.dev0/tests/roundtrip/__init__.py +1 -0
  109. xtalate-0.1.0.dev0/tests/roundtrip/test_identity.py +120 -0
  110. xtalate-0.1.0.dev0/tests/schema/__init__.py +0 -0
  111. xtalate-0.1.0.dev0/tests/schema/test_arrays.py +72 -0
  112. xtalate-0.1.0.dev0/tests/schema/test_golden_roundtrip.py +70 -0
  113. xtalate-0.1.0.dev0/tests/schema/test_models.py +158 -0
  114. xtalate-0.1.0.dev0/tests/schema/test_paths.py +60 -0
  115. xtalate-0.1.0.dev0/tests/schema/test_presence.py +115 -0
  116. xtalate-0.1.0.dev0/tests/sdk/__init__.py +0 -0
  117. xtalate-0.1.0.dev0/tests/sdk/test_contracts.py +129 -0
  118. xtalate-0.1.0.dev0/tests/test_smoke.py +38 -0
  119. xtalate-0.1.0.dev0/tests/validation/__init__.py +0 -0
  120. xtalate-0.1.0.dev0/tests/validation/test_engine.py +222 -0
  121. xtalate-0.1.0.dev0/tests/validation/test_rethreshold.py +77 -0
  122. xtalate-0.1.0.dev0/tests/validation/test_tolerance.py +56 -0
@@ -0,0 +1,39 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(grep -n '^#\\\\{1,3\\\\} ' /Users/ailsayilu/ChemBridge/docs/MASTER_SPEC.md)",
5
+ "Bash(python3 -m venv /tmp/cb-venv)",
6
+ "Bash(/tmp/cb-venv/bin/python -m pip install --upgrade pip -q)",
7
+ "Bash(/tmp/cb-venv/bin/pip install *)",
8
+ "Bash(/tmp/cb-venv/bin/ruff check *)",
9
+ "Bash(/tmp/cb-venv/bin/ruff format *)",
10
+ "Bash(/tmp/cb-venv/bin/mypy)",
11
+ "Bash(/tmp/cb-venv/bin/lint-imports)",
12
+ "Bash(/tmp/cb-venv/bin/pytest -q)",
13
+ "Bash(/tmp/cb-venv/bin/chembridge)",
14
+ "Bash(git checkout *)",
15
+ "Bash(git add *)",
16
+ "Bash(git commit *)",
17
+ "Bash(rm -rf /tmp/cb-venv && echo \"venv removed\" && git log --oneline -1 && git branch --show-current)",
18
+ "Bash(pip show *)",
19
+ "Bash(python3 -m venv .venv)",
20
+ "Bash(.venv/bin/python -m pip install --upgrade pip -q)",
21
+ "Bash(.venv/bin/pip install *)",
22
+ "Bash(.venv/bin/python --version)",
23
+ "Bash(.venv/bin/python -c \"import chembridge; print\\('chembridge', chembridge.__version__, 'importable from', chembridge.__file__\\)\")",
24
+ "Bash(.venv/bin/ruff check *)",
25
+ "Bash(.venv/bin/ruff format *)",
26
+ "Bash(.venv/bin/mypy)",
27
+ "Bash(.venv/bin/lint-imports)",
28
+ "Bash(.venv/bin/pytest -q)",
29
+ "Bash(.venv/bin/python -c \"import yaml,sys\")",
30
+ "Bash(.venv/bin/python -c \"import yaml; d=yaml.safe_load\\(open\\('.github/workflows/ci.yml'\\)\\); print\\(' matrix:', d['jobs']['lint-and-test']['strategy']['matrix']['python-version']\\)\")",
31
+ "Bash(python3 -c \"import yaml; d=yaml.safe_load\\(open\\('.github/workflows/ci.yml'\\)\\); print\\(' matrix:', d['jobs']['lint-and-test']['strategy']['matrix']['python-version']\\)\")",
32
+ "Bash(git config *)",
33
+ "Bash(echo \"name : $\\(git config user.name\\)\")",
34
+ "Bash(echo \"email: $\\(git config user.email\\)\")",
35
+ "Bash(git rebase *)",
36
+ "Bash(git fetch *)"
37
+ ]
38
+ }
39
+ }
@@ -0,0 +1,48 @@
1
+ name: ci
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [main]
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ lint-and-test:
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ fail-fast: false
17
+ matrix:
18
+ # 3.11 is the supported floor (catches use of newer syntax); 3.13 is the
19
+ # local dev interpreter (DECISIONS.md D11) — testing both keeps dev and CI
20
+ # from drifting.
21
+ python-version: ["3.11", "3.13"]
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python-version }}
28
+ cache: pip
29
+
30
+ - name: Install
31
+ run: |
32
+ python -m pip install --upgrade pip
33
+ pip install -e ".[dev]"
34
+
35
+ - name: Ruff (lint)
36
+ run: ruff check .
37
+
38
+ - name: Ruff (format)
39
+ run: ruff format --check .
40
+
41
+ - name: Mypy
42
+ run: mypy
43
+
44
+ - name: Import-graph lint (P2)
45
+ run: lint-imports
46
+
47
+ - name: Pytest
48
+ run: pytest
@@ -0,0 +1,230 @@
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
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # Marimo
213
+ marimo/_static/
214
+ marimo/_lsp/
215
+ __marimo__/
216
+
217
+ # Streamlit
218
+ .streamlit/secrets.toml
219
+
220
+ #MacOS
221
+ .DS_Store
222
+
223
+ # Secrets (CLAUDE.md "Never commit secrets" / DECISIONS.md) — defensive, ahead of need:
224
+ # no credentials exist in the v0.1 codebase yet, but these patterns are excluded before
225
+ # the v0.5 Service layer (DB, object storage, hosted-instance API keys) makes them real.
226
+ *.pem
227
+ *.key
228
+ credentials.json
229
+ secrets.*
230
+ !secrets.*.example
@@ -0,0 +1,2 @@
1
+ # Automatically created by Grimp.
2
+ *
@@ -0,0 +1 @@
1
+ {"xtalate.parsers":[["xtalate.parsers.extxyz",14,"from xtalate.parsers.extxyz import ExtxyzParser"],["xtalate.sdk",17,"from xtalate.sdk import ParserPlugin"],["xtalate.parsers.poscar",15,"from xtalate.parsers.poscar import PoscarParser, make_contcar_parser, make_poscar_parser"],["xtalate.parsers.xyz",16,"from xtalate.parsers.xyz import XyzParser"]],"xtalate.parsers._common":[["xtalate.schema",15,"from xtalate.schema import ConversionRecord, Provenance"],["xtalate",14,"from xtalate import __version__"]],"xtalate.recovery.scenarios":[],"xtalate.recovery":[["xtalate.recovery.engine",15,"from xtalate.recovery.engine import ("],["xtalate.recovery.scenarios",23,"from xtalate.recovery.scenarios import ("]],"xtalate.exporters.poscar":[["xtalate.schema",21,"from xtalate.schema import CanonicalObject"],["xtalate.sdk",22,"from xtalate.sdk import ("]],"xtalate.validation":[["xtalate.validation.engine",13,"from xtalate.validation.engine import ConversionReportView, ValidationEngine"],["xtalate.validation.report",14,"from xtalate.validation.report import CheckResult, ValidationReport"],["xtalate.validation.tolerance",16,"from xtalate.validation.tolerance import ToleranceProfile"],["xtalate.validation.rethreshold",15,"from xtalate.validation.rethreshold import rethreshold"]],"xtalate.validation.tolerance":[],"xtalate.discovery.sniffer":[["xtalate.capabilities",26,"from xtalate.capabilities import Registry"]],"xtalate.schema.presence":[["xtalate.schema.models",30,"from xtalate.schema.models import CanonicalObject, Frame"]],"xtalate.capabilities":[["xtalate.capabilities.registry",9,"from xtalate.capabilities.registry import ("]],"xtalate.discovery.engine":[["xtalate.sdk",25,"from xtalate.sdk import CapabilityLevel, ParseError, ParseIssue"],["xtalate.capabilities",21,"from xtalate.capabilities import Registry"],["xtalate.schema",24,"from xtalate.schema import CanonicalObject"],["xtalate.discovery.sniffer",23,"from xtalate.discovery.sniffer import Sniffer, SniffResult"],["xtalate.discovery.report",22,"from xtalate.discovery.report import DiscoveryReport, FieldPresenceEntry"]],"xtalate":[],"xtalate.schema":[["xtalate.schema.models",7,"from xtalate.schema.models import ("],["xtalate.schema.presence",22,"from xtalate.schema.presence import PathPresence, PresenceMap"]],"xtalate.exporters":[["xtalate.exporters.poscar",14,"from xtalate.exporters.poscar import ("],["xtalate.exporters.extxyz",13,"from xtalate.exporters.extxyz import ExtxyzExporter"],["xtalate.exporters.xyz",19,"from xtalate.exporters.xyz import XyzExporter"],["xtalate.sdk",20,"from xtalate.sdk import ExporterPlugin"]],"xtalate.schema.arrays":[],"xtalate.conversion.report":[],"xtalate.sdk":[["xtalate.sdk.plugins",15,"from xtalate.sdk.plugins import ExporterPlugin, ParserPlugin"],["xtalate.sdk.results",16,"from xtalate.sdk.results import ParseError, ParseIssue, ParseResult"],["xtalate.sdk.capabilities",10,"from xtalate.sdk.capabilities import ("]],"xtalate.parsers.poscar":[["xtalate.schema",26,"from xtalate.schema import ("],["xtalate.parsers._common",25,"from xtalate.parsers._common import build_provenance"],["xtalate.schema.elements",36,"from xtalate.schema.elements import is_valid_symbol"],["xtalate.sdk",37,"from xtalate.sdk import ("]],"xtalate.validation.report":[["xtalate.sdk",15,"from xtalate.sdk import ParseIssue"]],"xtalate.cli.render":[["xtalate.discovery.report",12,"from xtalate.discovery.report import DiscoveryReport"],["xtalate.sdk",13,"from xtalate.sdk import CapabilityLevel, FormatCapabilities"],["xtalate.conversion.report",11,"from xtalate.conversion.report import ConversionReport"],["xtalate.validation.report",14,"from xtalate.validation.report import ValidationReport"]],"xtalate.discovery.report":[["xtalate.sdk",22,"from xtalate.sdk import CapabilityLevel, ParseIssue"]],"xtalate.sdk.capabilities":[],"xtalate.schema.elements":[],"xtalate.discovery":[["xtalate.discovery.report",10,"from xtalate.discovery.report import DiscoveryReport, FieldPresenceEntry"],["xtalate.discovery.sniffer",11,"from xtalate.discovery.sniffer import ("],["xtalate.discovery.engine",9,"from xtalate.discovery.engine import DiscoveryEngine"]],"xtalate.validation.engine":[["xtalate.schema",32,"from xtalate.schema import CanonicalObject"],["xtalate.capabilities",31,"from xtalate.capabilities import Registry"],["xtalate.sdk",33,"from xtalate.sdk import ParseError, ParseIssue"],["xtalate.validation.tolerance",35,"from xtalate.validation.tolerance import ToleranceProfile"],["xtalate.validation.report",34,"from xtalate.validation.report import CheckResult, ValidationReport"]],"xtalate.cli.main":[["xtalate.discovery",32,"from xtalate.discovery import DiscoveryEngine"],["xtalate.capabilities",24,"from xtalate.capabilities import Registry"],["xtalate.sdk",34,"from xtalate.sdk import ParseError"],["xtalate.registry",33,"from xtalate.registry import default_registry"],["xtalate.validation.rethreshold",35,"from xtalate.validation import ("],["xtalate.discovery",253,"from xtalate.discovery import Sniffer"],["xtalate.sdk",273,"from xtalate.sdk import ParseIssue"],["xtalate.cli.render",25,"from xtalate.cli import render"],["xtalate.validation",35,"from xtalate.validation import ("],["xtalate",23,"from xtalate import __version__"],["xtalate.conversion",26,"from xtalate.conversion import ("]],"xtalate.sdk.plugins":[["xtalate.sdk.results",17,"from xtalate.sdk.results import ParseResult"],["xtalate.schema",15,"from xtalate.schema import CanonicalObject"],["xtalate.sdk.capabilities",16,"from xtalate.sdk.capabilities import FormatCapabilities"]],"xtalate.parsers.extxyz":[["xtalate.parsers._common",47,"from xtalate.parsers._common import build_provenance"],["xtalate.schema",48,"from xtalate.schema import ("],["xtalate.sdk",58,"from xtalate.sdk import ("]],"xtalate.registry":[["xtalate.parsers",18,"from xtalate.parsers import builtin_parsers"],["xtalate.capabilities",16,"from xtalate.capabilities import Registry"],["xtalate.exporters",17,"from xtalate.exporters import builtin_exporters"]],"xtalate.conversion.preflight":[["xtalate.recovery",28,"from xtalate.recovery import UnresolvedScenario"],["xtalate.capabilities",26,"from xtalate.capabilities import CapabilityMatrix"],["xtalate.sdk",30,"from xtalate.sdk import CapabilityLevel"],["xtalate.schema",29,"from xtalate.schema import CanonicalObject"],["xtalate.conversion.report",27,"from xtalate.conversion.report import PreservedEntry, RemovedEntry, ReportWarning"]],"xtalate.recovery.engine":[["xtalate.recovery.scenarios",28,"from xtalate.recovery.scenarios import ("],["xtalate.schema",33,"from xtalate.schema import AtomsBlock, CanonicalObject, Cell, Frame"]],"xtalate.sdk.results":[["xtalate.schema",15,"from xtalate.schema import CanonicalObject"]],"xtalate.exporters.xyz":[["xtalate.schema",16,"from xtalate.schema import CanonicalObject"],["xtalate.sdk",17,"from xtalate.sdk import ("]],"xtalate.schema.paths":[["xtalate.schema.models",19,"from xtalate.schema.models import ("]],"xtalate.capabilities.registry":[["xtalate.schema.paths",16,"from xtalate.schema.paths import expand_capability_path, is_valid_path"],["xtalate.sdk",17,"from xtalate.sdk import ("]],"xtalate.parsers.xyz":[["xtalate.schema",30,"from xtalate.schema import ("],["xtalate.sdk",38,"from xtalate.sdk import ("],["xtalate.parsers._common",29,"from xtalate.parsers._common import build_provenance"],["xtalate.schema.elements",37,"from xtalate.schema.elements import is_valid_symbol"]],"xtalate.exporters.extxyz":[["xtalate.schema",22,"from xtalate.schema import CanonicalObject, Frame"],["xtalate.sdk",23,"from xtalate.sdk import ("]],"xtalate.conversion.engine":[["xtalate.sdk",58,"from xtalate.sdk import ParseIssue"],["xtalate",36,"from xtalate import __version__"],["xtalate.validation",59,"from xtalate.validation import ToleranceProfile, ValidationEngine, ValidationReport"],["xtalate.conversion.preflight",38,"from xtalate.conversion.preflight import PreflightDiff, build_preflight"],["xtalate.recovery",45,"from xtalate.recovery import RecoveryEngine"],["xtalate.capabilities",37,"from xtalate.capabilities import Registry"],["xtalate.conversion.report",383,"from xtalate.conversion.report import ReportWarning"],["xtalate.conversion.report",39,"from xtalate.conversion.report import ("],["xtalate.schema",46,"from xtalate.schema import ("]],"xtalate.schema.models":[["xtalate.schema.arrays",26,"from xtalate.schema.arrays import ("],["xtalate.schema.presence",248,"from xtalate.schema.presence import compute_field_presence"],["xtalate.schema.elements",33,"from xtalate.schema.elements import atomic_number, is_valid_symbol"],["xtalate.schema.presence",24,"from xtalate.schema.presence import PresenceMap"]],"xtalate.validation.rethreshold":[["xtalate.validation.tolerance",21,"from xtalate.validation.tolerance import ToleranceProfile"],["xtalate.validation.report",20,"from xtalate.validation.report import CheckResult, ValidationReport"]],"xtalate.conversion":[["xtalate.conversion.preflight",18,"from xtalate.conversion.preflight import ("],["xtalate.conversion.engine",12,"from xtalate.conversion.engine import ("],["xtalate.recovery",31,"from xtalate.recovery import UnresolvedScenario"],["xtalate.conversion.report",23,"from xtalate.conversion.report import ("]],"xtalate.cli":[["xtalate.cli.main",9,"from xtalate.cli.main import main"]]}
@@ -0,0 +1,3 @@
1
+ Signature: 8a477f597d28d172789f06886806bc55
2
+ # This file is a cache directory tag automatically created by Grimp.
3
+ # For information about cache directory tags see https://bford.info/cachedir/
@@ -0,0 +1 @@
1
+ {"xtalate.schema.presence": 1783833043.4318962, "xtalate.schema.arrays": 1783528075.2865715, "xtalate.schema.elements": 1783528075.2868724, "xtalate.exporters.xyz": 1783833043.4248168, "xtalate.sdk.plugins": 1783833043.4329252, "xtalate": 1783833043.4171271, "xtalate.conversion": 1783833043.4197884, "xtalate.discovery": 1783833043.4215312, "xtalate.cli.render": 1783833043.4192166, "xtalate.discovery.report": 1783833043.4224696, "xtalate.validation": 1783833043.4333227, "xtalate.validation.report": 1783833043.4348226, "xtalate.recovery": 1783833043.428564, "xtalate.exporters.poscar": 1783833043.4242523, "xtalate.recovery.scenarios": 1783833043.4294102, "xtalate.conversion.preflight": 1783833043.4210417, "xtalate.conversion.report": 1783830156.67924, "xtalate.parsers.xyz": 1783833043.427966, "xtalate.sdk.results": 1783833043.4331102, "xtalate.validation.engine": 1783833043.4338317, "xtalate.sdk.capabilities": 1783833043.4327276, "xtalate.parsers._common": 1783833043.4252386, "xtalate.schema": 1783833043.4300969, "xtalate.sdk": 1783833043.4324012, "xtalate.exporters": 1783833043.4230797, "xtalate.validation.tolerance": 1783830156.685212, "xtalate.capabilities.registry": 1783833043.4178298, "xtalate.capabilities": 1783833043.4173203, "xtalate.cli": 1783833043.4184995, "xtalate.exporters.extxyz": 1783833043.423647, "xtalate.schema.paths": 1783833043.431377, "xtalate.registry": 1783833043.429924, "xtalate.parsers.extxyz": 1783833043.4258244, "xtalate.parsers": 1783833043.4250007, "xtalate.recovery.engine": 1783833043.428934, "xtalate.discovery.engine": 1783833043.4218535, "xtalate.conversion.engine": 1783833043.4202921, "xtalate.discovery.sniffer": 1783833043.4227653, "xtalate.parsers.poscar": 1783833043.4269419, "xtalate.cli.main": 1783833043.4189324, "xtalate.validation.rethreshold": 1783833043.435106, "xtalate.schema.models": 1783833043.4308712}
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ All notable changes to Xtalate are recorded here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project adheres to
5
+ [Semantic Versioning](https://semver.org/spec/v2.0.0.html). The canonical schema version is
6
+ tracked separately from the package version and reaches `1.0.0` only in the v1.0 release
7
+ (`docs/MASTER_SPEC.md` Part 2 §5); v0.1 objects carry `schema_version = "0.1.0"`.
8
+
9
+ ## [0.1.0] — 2026-07-10
10
+
11
+ First release: the complete pure-Python **library + CLI** core. It converts between four
12
+ computational-chemistry formats while reporting — and independently re-validating — every
13
+ byte of scientific information kept, dropped, or fabricated.
14
+
15
+ ### Added
16
+
17
+ - **Canonical Data Model** (`xtalate.schema`) — the single internal schema every parser
18
+ writes and every exporter reads, with the normative absence convention (`None` = "never in
19
+ the source" vs a real zero) and an on-demand `field_presence()` introspection.
20
+ - **Plugin SDK, Format Sniffer, and Capability Matrix** (`xtalate.sdk`,
21
+ `xtalate.discovery`, `xtalate.capabilities`) — stable parser/exporter contracts, a
22
+ generic confidence-scored sniffer, and a per-format, per-field read/write capability registry.
23
+ - **Formats** (`xtalate.parsers`, `xtalate.exporters`) — read and write for plain **XYZ**,
24
+ **extended XYZ** (ASE-backed, with default-laundering), **POSCAR**, and **CONTCAR**, each with a
25
+ golden round-trip and error-fixture suite.
26
+ - **Information Discovery Engine** (`xtalate.discovery`) — the ✓/✗ Discovery Report: a file's
27
+ canonical-field inventory annotated with the detected format's read capability.
28
+ - **Conversion Engine** (`xtalate.conversion`) — the pre-flight capability diff, the
29
+ `write_plan` discipline (materialized as a filtered `canonical′`), the Conversion Report, and
30
+ the completeness invariant enforced as an always-on runtime assertion.
31
+ - **Recovery Engine** (`xtalate.recovery`) — explicit, preset-only resolution of the
32
+ `frame_selection` and `missing_lattice` scenarios under the three-way hazard model; every
33
+ choice recorded as an Assumption, and a structured **refusal** when no choice is supplied.
34
+ - **Validation Engine** (`xtalate.validation`) — the unconditional post-conversion re-parse
35
+ and nine-check diff (Part 5 §2) under named tolerance profiles (`default`/`strict`/`loose`),
36
+ plus stored-report re-thresholding.
37
+ - **CLI** (`xtalate`) — `inspect`, `convert`, `validate`, and `capabilities`, with the
38
+ CI-native exit-code contract (`0`/`2`/`3`/`4`/`5`/`1`) and `--json` structured output.
39
+
40
+ ### Known limitations (v0.1 scope)
41
+
42
+ - No web service, REST API, or UI (v0.5 / v0.6).
43
+ - CIF, XDATCAR, and ASE `.traj` — the remaining Phase-1 formats — are not yet implemented (v0.2+).
44
+ - Recovery is preset-only; tolerance profiles are the three named ones (custom tables are later
45
+ seams).
46
+
47
+ [0.1.0]: https://github.com/jsong1218/Xtalate/releases/tag/v0.1.0
@@ -0,0 +1,23 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use Xtalate in your research, please cite it as below."
3
+ title: "Xtalate: an audit-first converter for computational-chemistry file formats"
4
+ abstract: >-
5
+ Xtalate is a translation layer between computational-chemistry file formats that reports
6
+ exactly what each conversion keeps, loses, or fabricates, and independently re-validates the
7
+ result. All translation flows through a single canonical data model; loss is predicted before
8
+ conversion and verified after.
9
+ type: software
10
+ authors:
11
+ - family-names: Song
12
+ given-names: Jiayu
13
+ version: 0.1.0
14
+ date-released: 2026-07-10
15
+ license: Apache-2.0
16
+ repository-code: "https://github.com/jsong1218/Xtalate"
17
+ keywords:
18
+ - computational chemistry
19
+ - file format conversion
20
+ - materials science
21
+ - data provenance
22
+ - VASP
23
+ - ASE
@@ -0,0 +1,145 @@
1
+ # CLAUDE.md — Xtalate Project Context
2
+
3
+ > Load this file at the start of every Claude Code / Claude chat session working on Xtalate. It is a compressed index of `docs/MASTER_SPEC.md` (the full constitution — a single edited document, *not* an assembly of standalone files; see the document-family section below). If anything here conflicts with a doc in `docs/`, **the doc wins** — this file is a map, not the territory. If a later-uploaded doc contradicts an earlier one, flag the discrepancy; do not silently pick one.
4
+
5
+ > **Status (refreshed Revision 1.6, July 2026).** **v0.1 is feature-complete** (MASTER_SPEC Preface Revision 1.2 item 17): the full spine — parse → pre-flight → recovery → export → report → validation — plus the Information Discovery Engine and the `xtalate` CLI are implemented, tested, and shipped for the four v0.1 formats (XYZ, extXYZ, POSCAR, CONTCAR). The map below is now current against Revisions 1.3–1.6 and `docs/DECISIONS.md` D1–D33; the two sections previously flagged HISTORICAL (Repository Shape, Documentation Set) have been rewritten to the real state. Mission, principles (P1–P6), glossary, architecture, the absence convention, tech stack (a *destination*, not the v0.1 dependency set), and API conventions remain binding. For current status and build-time rationale, read the **Revision 1.2 addenda** in the MASTER_SPEC Preface (the M0–M6 landing notes) and the **D-log through D33**; per-version execution lives in `docs/IMPLEMENTATION_PLAN_v0.1.md`–`_v1.0.md`.
6
+
7
+ ## Mission
8
+
9
+ > **Xtalate is the trusted translation layer between computational chemistry file formats — a converter that tells you exactly what it kept, what it lost, and why.**
10
+
11
+ Guiding philosophy: **never silently lose scientific information.** Litmus test for any design decision: *if a user diffed the source and output files by hand, would anything surprise them that Xtalate didn't already tell them about?* If yes, the design is wrong.
12
+
13
+ ## Design Principles (P1–P6) — binding, referenced by ID in every doc
14
+
15
+ | ID | Principle |
16
+ |----|-----------|
17
+ | **P1** | Every loss is reported, never assumed. "The user probably doesn't need X" is never a valid reason for silence. |
18
+ | **P2** | No parser talks to another parser. All translation flows through one Canonical Model: `Native File → Canonical Object → Target Format`. No format-to-format shortcuts, ever. |
19
+ | **P3** | Absence is information. The schema distinguishes "never present in the source" (`None`) from "present with value zero/empty." |
20
+ | **P4** | Recover explicitly, never guess. Missing-but-required data is supplied only through an explicit Recovery Workflow, recorded as an Assumption. |
21
+ | **P5** | Know the formats before converting. The Capability Matrix predicts preservation/loss *before* conversion; Validation verifies it *after*. |
22
+ | **P6** | Extensibility over optimization. New formats/features attach at defined seams without core redesign; performance is reclaimed later behind stable interfaces. |
23
+
24
+ One sentence to carry into every doc: **a conversion you can't audit is a conversion you can't trust.**
25
+
26
+ ## Binding Glossary (use these terms verbatim; never coin synonyms)
27
+
28
+ | Term | Meaning |
29
+ |---|---|
30
+ | **Canonical Model / Canonical Object** | The single internal schema every parser writes to and every exporter reads from (defined in `02_Canonical_Data_Model.md`). |
31
+ | **Parser** | Reads exactly one native format → produces a Canonical Object. Never writes files, never calls another parser, never defaults an absent field. |
32
+ | **Exporter** | Reads a Canonical Object → writes exactly one native format. Never reads native files. |
33
+ | **Capability Matrix** | Per-format, per-field data structure declaring what each format can/cannot express; drives loss prediction. |
34
+ | **Information Discovery Engine** | Inspects an arbitrary file and reports what's present/absent (✓/✗ per canonical field) without converting it. |
35
+ | **Recovery Engine / Recovery Workflow** | Offers explicit, user-chosen ways to supply data a target format requires but the source lacks; every choice becomes an Assumption. |
36
+ | **Conversion Report** | Structured, machine-readable record of what a conversion kept, dropped, transformed, or assumed. |
37
+ | **Validation Report** | Post-conversion re-parse-and-diff result against the original Canonical Object, within tolerance. |
38
+ | **Discovery Report** | Output of the Information Discovery Engine. |
39
+ | **Provenance** | Canonical Model's record of source software, parser version, and full conversion history (incl. all Assumptions). |
40
+ | **Plugin SDK** | Stable interface for third-party parsers/exporters/analysis modules; first-party formats hold no privileged API. |
41
+ | **Round-trip** | `Format A → Canonical → Format B → Canonical`, diffed within tolerance; primary strategy for catching silent bugs. |
42
+ | **Phase 1 formats** | The seven formats: XYZ, extXYZ, CIF, POSCAR, CONTCAR, XDATCAR, ASE trajectory. (Under the roadmap ladder these do not all land in the "MVP": roadmap v0.2's MVP is four of them — XYZ, extXYZ, POSCAR, CONTCAR, the v0.1 set — and all seven complete at roadmap v0.4. Say "the seven Phase 1 formats," not "the seven MVP formats.") |
43
+
44
+ ## Architecture at a Glance
45
+
46
+ Single spine, four advisory subsystems that guide but never bypass it:
47
+
48
+ ```
49
+ Native File → Format Sniffer → Parser → Canonical Object → Exporter → Target Format
50
+ ↑ ↓
51
+ Information Discovery Capability Matrix
52
+ Recovery Engine (explicit only) → Validation Engine
53
+ ```
54
+
55
+ - **Format Sniffer** — identifies format + confidence, selects a parser.
56
+ - **Parser layer** (one per format) — must not read another format or talk to another parser.
57
+ - **Canonical Object** — the only thing that crosses the parser/exporter boundary.
58
+ - **Capability Matrix** — tells the exporter what the target can hold *before* it writes.
59
+ - **Recovery Engine** — fills required-but-missing fields only with explicit user consent, recorded as an Assumption.
60
+ - **Validation Engine** — re-parses the output and diffs it against the source Canonical Object.
61
+ - **API layer** (`backend/`, FastAPI) — thin; validates requests, manages jobs/storage; contains no scientific logic.
62
+ - **Web UI** (`frontend/`, Next.js) — presents the workflow and renders reports faithfully; never re-implements conversion logic or hides losses.
63
+
64
+ ## Canonical Model — Absence Convention (P3), normative
65
+
66
+ | State | Representation | Meaning |
67
+ |---|---|---|
68
+ | Absent | `None` | Source file did not contain this information at all. |
69
+ | Present | Actual value (incl. zeros) | Source file contained it; the value is the value. |
70
+
71
+ Parsers are forbidden from defaulting — no zero velocities, no identity lattice, no `energy = 0.0` invented to fill a gap. Schema categories: Geometry, Simulation Cell, Trajectory, Dynamics, Electronic Information, Simulation Metadata, Provenance, User Metadata. Modeled as pydantic v2-style; NumPy arrays in memory, nested JSON lists on the wire; internal coordinates and unit conventions are defined normatively in `02_Canonical_Data_Model.md` — treat field names there as final.
72
+
73
+ ## Tech Stack (rationale lives in `01_Architecture.md §4`)
74
+
75
+ > These are destination choices for the versions that need them (Service in v0.5, Web UI in v0.6), not the v0.1 dependency set. v0.1 is a pure-Python library + CLI: pydantic + numpy, plus ASE once the extXYZ parser lands (the sole scientific dependency — pymatgen was rejected as a v0.1 dependency; see `docs/DECISIONS.md` D4, D7).
76
+
77
+ | Layer | Choice |
78
+ |---|---|
79
+ | Frontend | Next.js + React + Tailwind |
80
+ | Backend | FastAPI (Python — must be in-process with ASE/pymatgen) |
81
+ | Scientific core | ASE + pymatgen |
82
+ | Future visualization | Mol* (secondary goal, not MVP) |
83
+ | Relational DB | PostgreSQL (JSONB for report bodies) |
84
+ | Blob storage | S3-compatible object storage, lifecycle-expiring |
85
+ | Job queue | RQ on Redis |
86
+
87
+ ## Repository Shape (current v0.1 layout)
88
+
89
+ > The pre-implementation architecture review rejected a solo-maintainer monorepo of separately-packaged components as unnecessary overhead (`docs/ARCHITECTURE_REVIEW.md` §4.1; `docs/DECISIONS.md` D1). The shipped v0.1 layout is **one package** in a `src/` layout — no per-component `pyproject.toml`s. `frontend/`, `backend/`, and `plugins/` do not exist yet; they arrive with the versions that need them (Service at roadmap v0.5, Web UI at v0.6, entry-point plugin discovery at v0.3). `docs/MASTER_SPEC.md` Part 1 §5 is the authoritative tree.
90
+
91
+ ```
92
+ src/xtalate/
93
+ schema/ Canonical Model; depends on nothing else in xtalate/ (a.k.a. "canonical-schema")
94
+ sdk/ stable parser/exporter base classes + capability models (a.k.a. "plugin-sdk")
95
+ parsers/ one per format; depends only on schema + sdk
96
+ exporters/ one per format; depends only on schema + sdk
97
+ capabilities/ Capability Matrix: assembles + queries declarations (a.k.a. "capability-matrix")
98
+ discovery/ Information Discovery Engine + Format Sniffer + Discovery Report
99
+ conversion/ Conversion Engine — orchestrates the above + Conversion Report
100
+ recovery/ Recovery Engine + workflows (preset-only in v0.1)
101
+ validation/ Validation Engine + Validation Report
102
+ cli/ thin argparse presenter: inspect / convert / validate / capabilities
103
+ examples/ runnable end-to-end library + CLI samples
104
+ tests/ golden/, roundtrip/, and per-subpackage suites
105
+ docs/ the doc family this file indexes
106
+ ```
107
+
108
+ Dependency direction is strict and acyclic — enforced physically by an `import-linter` `layers` contract (`pyproject.toml`), run with ruff + mypy --strict + pytest on every PR (`docs/DECISIONS.md` D5). This is what enforces **P2**. The descriptive names in parentheses (`canonical-schema`, `plugin-sdk`, `capability-matrix`) are the prose labels used across the spec; the tree gives the import-name mapping.
109
+
110
+ ## API Conventions (full spec in `06_API.md`)
111
+
112
+ - All endpoints under `/v1/`. Path-prefix versioning; additive changes are non-breaking; new formats/scenarios are *values*, not new endpoints.
113
+ - Long-running operations (`inspect`, `convert`, `validate`) are **async jobs**: submit → poll `/v1/jobs/{job_id}` → retrieve result.
114
+ - Job states: `queued → running → completed | failed | cancelled`, plus `queued → failed` (dequeue-precondition failure, Revision 1.1) and `running → awaiting_recovery → running | expired`. An expired `awaiting_recovery` job resolves to a **refused** conversion, never a silently applied default.
115
+ - **A refusal is not an HTTP error.** A conversion the engine declines is a completed job whose `ConversionReport.status == "refused"` — HTTP 200.
116
+ - Single error envelope for all non-2xx responses: `{ error: { code, message, details, request_id, documentation_url } }`. Codes are stable machine strings (e.g. `UNKNOWN_FORMAT`, `PARSE_ERROR`, `VALIDATION_ACK_REQUIRED`, `JOB_ALREADY_TERMINAL`).
117
+ - Response bodies embed the pydantic report models verbatim (`DiscoveryReport`, `ConversionReport`, `ValidationReport`) — no parallel DTOs.
118
+
119
+ *(This describes the v0.5 Service layer's target contract; there is no API yet in v0.1.)*
120
+
121
+ ## Documentation Set
122
+
123
+ > `docs/MASTER_SPEC.md` is edited directly as the single source of truth (Revision 1.2, Preface); the standalone `00`–`10` `.md` files named in older prose **never existed as separate committed files** — do not create or assume them. The Part numbering (`04 §3.3` = "Part 4 §3.3") survives only as a stable citation scheme inside the one document. The document family that actually exists:
124
+
125
+ - **`docs/MASTER_SPEC.md`** — the constitution (Parts 0–10 + Appendices; Preface revision log runs Revisions 1.1–1.6). Start here; use its Table of Contents.
126
+ - **`docs/DECISIONS.md`** — build-time decisions log, **D1–D33**, each with a rejected alternative and a standing-rules note at the top.
127
+ - **`docs/ARCHITECTURE_REVIEW.md`** — the accepted pre-implementation review (its acceptance is MASTER_SPEC Revision 1.2).
128
+ - **`docs/Incremental_Roadmap_v1.0.md`** — the solo-developer version ladder (v0.1–v0.7); carries a Revision 1.6 staleness banner over its execution detail, ladder still binding.
129
+ - **`docs/IMPLEMENTATION_PLAN_v0.1.md` … `_v1.0.md`** — per-version execution plans, milestones **M0–M38**, each superseding the roadmap's execution prose for its version.
130
+ - **`docs/DOCS_CONSISTENCY_REVIEW_2026-07.md`** — the post-v0.1 corpus consistency review (findings C1–C11) this refresh implements.
131
+
132
+ > Not committed to this repo (external, historical): `Xtalate_Doc_Prompts.md` — the prompt set that generated the original drafts; superseded by the single-source-of-truth model, do not run it or create the standalone files.
133
+
134
+ ## Working Rules for This Project
135
+
136
+ - **Terminology is binding.** Reuse exact field names, endpoint paths, report field names, and component names already established. If a name seems wrong, say so explicitly and explain why — never rename silently.
137
+ - **Write for an isolated reader.** Every doc must stand alone for a contributor or a planning agent who has not read the master prompt directly, while staying consistent with the rest of `docs/`.
138
+ - **Justify nontrivial decisions.** Name at least one reasonable rejected alternative for each nontrivial architectural or design choice.
139
+ - **No silent data loss, ever — including in the docs themselves.** Any design that could cause silent loss must be called out explicitly, not glossed over.
140
+ - **MVP discipline.** Favor maintainability, correctness, and extensibility over convenience; don't overengineer Phase 1, but never foreclose Secondary Goals — name the extensibility seam they'd attach to.
141
+ - **Scope discipline.** Produce only the file(s) a given prompt asks for. Do not pad a doc with content that belongs in a different doc file.
142
+ - **Format.** Professional Markdown; Mermaid for all diagrams (GitHub-renderable); directory trees where relevant; no marketing language — this is engineering documentation, not a pitch.
143
+ - **No AI attribution in commits.** No `Co-Authored-By` AI trailer, no "Generated with …" line, and no AI listed as author or contributor in commit metadata, `CITATION.cff`, or release notes — the human maintainer is the author of record on every commit an agent creates or assists with. (`docs/MASTER_SPEC.md` Preface; `docs/DECISIONS.md` D10.)
144
+ - **Never commit secrets.** No API keys, tokens, database credentials, or other secrets are ever committed to this repository, in code, config, fixtures, or commit messages — not even temporarily, not even in a since-reverted commit (git history is not a safe place to "fix it later"). Secrets are supplied only via environment variables or an untracked local file (`.env`, already gitignored) and referenced by name in code, never inlined. Before staging or committing, check `git status`/`git diff` for anything that looks like a credential — including in filenames that look innocuous — and stop to ask if unsure. If a secret is ever accidentally committed, the fix is rotation (invalidate the leaked credential) *and* history rewrite, not just a follow-up commit that deletes it. There are no secrets in the v0.1 codebase (pure-Python library + CLI, no network calls, no credentials); this rule exists ahead of need because v0.5 (Service layer: database, object storage, hosted-instance API keys) is exactly where a leak would first become possible, and the discipline should already be established by then.
145
+